봇 개발
Bot API 레퍼런스
파발의 HTTP Bot API 가 받는 메서드와 주고받는 객체, 오류를 빠짐없이 정리했습니다. 텔레그램 Bot API 와 호환됩니다.
파발의 HTTP Bot API 는 텔레그램 Bot API 와 같은 주소 형식·요청·응답·오류를 씁니다. 이 문서는 파발이 실제로 받는 것만 적습니다. 여기 없는 메서드는 404 Not Found: method not found 를 돌려줍니다. 처음이라면 봇 만들기 튜토리얼부터 보세요.
요청 보내기
https://pabal.me/bot<토큰>/<메서드>
- HTTP 메서드:
GET과POST모두 됩니다. - 파라미터를 보내는 네 가지 방법 — 쿼리 문자열(
?chat_id=1&text=hi),application/x-www-form-urlencoded,application/json, 파일을 올릴 때multipart/form-data. 섞어 써도 됩니다. - 객체 파라미터(
reply_markup,commands,allowed_updates)는 JSON 본문에서는 객체·배열 그대로, 폼·쿼리에서는 JSON 문자열로 넣습니다. - 메서드 이름은 대소문자를 가리지 않습니다(
sendMessage=sendmessage). - 본문 크기는 12MB 까지입니다(넘으면
413). - 파일 내려받기는 따로
https://pabal.me/file/bot<토큰>/<file_path>입니다(getFile).
응답과 오류
응답은 늘 JSON 입니다. 성공이면 HTTP 200 과 result, 실패면 그 HTTP 상태와 같은 error_code, 사람이 읽을 description 이 옵니다.
{"ok": true, "result": { … }}{"ok": false, "error_code": 400, "description": "Bad Request: chat not found"}| error_code | 언제 | description 예 |
|---|---|---|
400 | 파라미터가 틀림 | 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 | 토큰이 틀림 | Unauthorized |
403 | 보낼 권한이 없음 | Forbidden: bot can't initiate conversation with a user, Forbidden: bot is not a member of the group chat |
404 | 없는 메서드·파일 | Not Found: method not found |
409 | 웹훅이 있는데 getUpdates | Conflict: can't use getUpdates method while webhook is active; use deleteWebhook to delete the webhook first |
413 | 본문이 12MB 를 넘음 | Request Entity Too Large |
500 | 서버 내부 오류 | Internal Server Error |
파발은 아직 요청 전체의 속도 제한을 두지 않지만, 음성 메서드에는 한 시간 한도가 있어 429 Too Many Requests: retry after N 이 옵니다(음성 통화). 429 를 받으면 parameters.retry_after 초만큼 기다렸다 다시 보내도록 짜 두세요.
업데이트 받기
두 방법 중 하나를 씁니다. 동시에 쓸 수는 없습니다.
getUpdates
POST/bot<토큰>/getUpdates
기다리는 업데이트를 가져옵니다(롱 폴링). 웹훅이 설정돼 있으면 409.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
offset | Integer | 선택 | 이 번호 이상의 업데이트만. 이보다 작은 것은 "받았음"으로 지워집니다. 처리한 마지막 update_id + 1 을 주세요. |
limit | Integer | 선택 | 1~100, 기본 100 |
timeout | Integer | 선택 | 기다릴 초, 0~50, 기본 0. 25~30 을 권합니다. |
allowed_updates | Array of String | 선택 | 받을 종류(Update). 비우면 기본 종류(message, edited_message, callback_query) 전부. 음성 종류는 이름을 적어야만 옵니다. 준 목록은 봇의 설정으로 남아 다음 호출과 웹훅에도 쓰입니다(주지 않으면 이전 값 그대로) |
돌려주는 것: Array of Update
setWebhook
POST/bot<토큰>/setWebhook
업데이트를 HTTPS 주소로 받습니다. 자세한 설명은 웹훅 문서에 있습니다.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
url | String | 예 | https:// 공인 주소. 빈 문자열이면 웹훅을 지움 |
secret_token | String | 선택 | 1~256자, A-Z a-z 0-9 _ -. 요청 헤더 X-Telegram-Bot-Api-Secret-Token 로 보냄 |
allowed_updates | Array of String | 선택 | message, edited_message, callback_query 중에서. 비우면 이 셋 전부. 음성 종류(voice_call, voice_call_tool_query, voice_call_transcript)는 이름을 적어야만 옵니다. 주지 않으면 이전 값 그대로 |
drop_pending_updates | Boolean | 선택 | 기다리던 업데이트를 버림 |
max_connections | Integer | 선택 | 1~100, 기본 40. 보관만 하고, 전달은 봇마다 한 번에 하나씩 |
certificate | InputFile | 안 됨 | 400 — 공인 인증서를 쓰세요 |
ip_address | String | 무시 |
돌려주는 것: True
deleteWebhook
POST/bot<토큰>/deleteWebhook
웹훅을 지우고 getUpdates 로 돌아갑니다. 웹훅이 없어도 성공합니다.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
drop_pending_updates | Boolean | 선택 | 기다리던 업데이트를 버림 |
돌려주는 것: True
getWebhookInfo
GET/bot<토큰>/getWebhookInfo
웹훅 상태. 파라미터 없음. 웹훅이 없으면 url 이 빈 문자열입니다.
돌려주는 것: WebhookInfo
메서드
| 메서드 | 하는 일 |
|---|---|
getMe | 봇 자신의 정보 |
sendMessage | 글 보내기 (버튼 포함) |
sendPhoto | 사진 보내기 |
sendDocument | 파일 보내기 |
sendLocation | 위치 보내기 (실시간 위치 포함) |
sendVenue | 장소 보내기 (이름과 주소가 붙은 위치) |
sendContact | 연락처 카드 보내기 |
editMessageLiveLocation · stopMessageLiveLocation | 실시간 위치 옮기기·멈추기 |
editMessageText | 보낸 메시지의 글·버튼 고치기 |
editMessageCaption | 사진 설명 고치기 |
editMessageReplyMarkup | 버튼만 고치기 |
deleteMessage | 메시지 지우기 |
answerCallbackQuery | 버튼 누름에 답하기 |
sendChatAction | "입력 중" 표시 (받기만 함) |
getChat | 대화 정보 |
getFile | 받은 사진의 내려받기 경로 |
setMyCommands · getMyCommands · deleteMyCommands | 명령어 메뉴 |
logOut · close | 호환용 (아무 일도 하지 않음) |
getUpdates · setWebhook · deleteWebhook · getWebhookInfo | 업데이트 받기 (위) |
setVoiceCallSettings · getVoiceCallSettings · endVoiceCall · sendVoiceCallContext · getVoiceCallUsage · startVoiceCall · answerVoiceCallToolQuery · setVoiceCallApiKey · setVoiceCallMcpServer … | 음성 봇 (아래 음성 통화) |
chat_id 에 넣을 수 있는 것
1:1 대화면 사람의 ID(양수), 기본 그룹이면 그룹 ID(음수), 또는 사람의 사용자명("@hana_lee"). 모두 업데이트의 chat.id 로 알 수 있습니다. 채널·슈퍼그룹 ID(-100…)는 아직 없습니다(400 chat not found).
getMe
GET/bot<토큰>/getMe
토큰이 맞는지 확인할 때 씁니다. 파라미터 없음.
돌려주는 것: User — 봇에게는 can_join_groups(true), can_read_all_group_messages(true), supports_inline_queries(false), can_connect_to_business(false), has_main_web_app(false) 가 더 붙습니다.
sendMessage
POST/bot<토큰>/sendMessage
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
chat_id | Integer 또는 String | 예 | 보낼 대화 (위 상자 참고) |
text | String | 예 | 1~4,096자. 글자 그대로 보냅니다 |
reply_markup | InlineKeyboardMarkup · ReplyKeyboardMarkup · ReplyKeyboardRemove · ForceReply | 선택 | 버튼 |
disable_notification | Boolean | 선택 | 소리 없이 보내기 |
parse_mode, entities, reply_parameters, link_preview_options … | 무시 | 받지만 쓰지 않습니다. 서식은 적용되지 않습니다 |
돌려주는 것: 보낸 Message
사람에게 보내려면 그 사람이 먼저 봇에게 말을 걸었어야 합니다(403 Forbidden: bot can't initiate conversation with a user). 그룹에 보내려면 봇이 그 그룹의 멤버여야 합니다.
sendPhoto
POST/bot<토큰>/sendPhoto
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
chat_id | Integer 또는 String | 예 | 보낼 대화 |
photo | InputFile 또는 String | 예 | multipart/form-data 로 올린 파일(10MB 까지, JPEG·PNG·GIF), 또는 전에 받은 사진의 file_id. URL 은 아직 안 됨 |
caption | String | 선택 | 0~1,024자 |
reply_markup | sendMessage 와 같음 | 선택 | |
disable_notification | Boolean | 선택 |
돌려주는 것: 보낸 Message (photo 에 새 file_id)
sendDocument
POST/bot<토큰>/sendDocument
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
chat_id | Integer 또는 String | 예 | 보낼 대화 |
document | InputFile 또는 String | 예 | multipart/form-data 로 올린 파일(형식 제한 없음, 20MB 까지), 또는 전에 받은 파일의 file_id. URL 은 아직 안 됨 |
filename | String | 선택 | 받는 쪽에 보일 이름. 없으면 올린 파일의 이름을 씁니다 |
caption | String | 선택 | 0~1,024자 |
reply_markup | sendMessage 와 같음 | 선택 | |
disable_notification | Boolean | 선택 | |
thumbnail, disable_content_type_detection | 무시 | 받지만 쓰지 않습니다 |
돌려주는 것: 보낸 Message (document 에 새 file_id)
형식(mime_type)은 파일 이름의 확장자에서 정합니다. 앱 쪽에서 보낸 파일은 2GB 까지지만, 봇이 HTTP 로 올리거나 내려받는 것은 텔레그램과 같이 20MB 까지입니다.
sendLocation
POST/bot<토큰>/sendLocation
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
chat_id | Integer 또는 String | 예 | 보낼 대화 |
latitude, longitude | Float | 예 | 위도(−90~90)와 경도(−180~180). 벗어나면 400 Bad Request: GEO_POINT_INVALID |
horizontal_accuracy | Float | 선택 | 오차 반경(미터, 0~1,500). 반올림해 정수로 저장합니다 |
live_period | Integer | 선택 | 주면 실시간 위치가 됩니다: 60~86,400초, 또는 멈출 때까지 2147483647(0x7FFFFFFF). 그 밖의 값은 400 Bad Request: wrong live_period |
heading | Integer | 선택 | 실시간 위치의 진행 방향, 1~360도 |
proximity_alert_radius | Integer | 선택 | 실시간 위치의 접근 알림 반경(미터). 저장하고 보여 주지만 알림은 아직 보내지 않습니다 |
reply_markup | sendMessage 와 같음 | 선택 | |
disable_notification | Boolean | 선택 |
돌려주는 것: 보낸 Message (location 에 Location)
sendVenue
POST/bot<토큰>/sendVenue
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
chat_id | Integer 또는 String | 예 | 보낼 대화 |
latitude, longitude | Float | 예 | 장소의 위치 (sendLocation 과 같음) |
title | String | 예 | 장소 이름 |
address | String | 예 | 주소 |
foursquare_id, foursquare_type | String | 선택 | Foursquare 의 장소 ID 와 종류. 받은 그대로 전합니다 |
google_place_id, google_place_type | String | 선택 | Google 장소 ID 와 종류. foursquare_id 가 없을 때만 씁니다 |
reply_markup | sendMessage 와 같음 | 선택 | |
disable_notification | Boolean | 선택 |
돌려주는 것: 보낸 Message (venue 에 Venue, location 에도 같은 위치)
title 이나 address 가 없으면 400 Bad Request: title and address are required 입니다.
sendContact
POST/bot<토큰>/sendContact
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
chat_id | Integer 또는 String | 예 | 보낼 대화 |
phone_number | String | 예 | 전화번호. 적힌 그대로 전합니다 |
first_name | String | 예 | 이름 |
last_name | String | 선택 | 성 |
vcard | String | 선택 | vCard 형식의 추가 정보, 64KB 까지 |
reply_markup | sendMessage 와 같음 | 선택 | |
disable_notification | Boolean | 선택 |
돌려주는 것: 보낸 Message (contact 에 Contact)
봇이 보낸 연락처 카드에는 user_id 가 붙지 않습니다. 번호가 파발 계정의 것이어도 그렇습니다 — 봇에게는 연락처가 없고, 붙여 주면 번호마다 한 번씩 불러 그 번호가 누구의 것인지 알아낼 수 있기 때문입니다. phone_number 나 first_name 이 없으면 400 Bad Request: phone_number and first_name are required 입니다.
editMessageLiveLocation
POST/bot<토큰>/editMessageLiveLocation
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
chat_id | Integer 또는 String | 예 | 메시지가 있는 대화 |
message_id | Integer | 예 | 옮길 실시간 위치 (봇이 보낸 것) |
latitude, longitude | Float | 예 | 새 위치 |
horizontal_accuracy | Float | 선택 | 오차 반경(미터, 0~1,500) |
heading | Integer | 선택 | 진행 방향, 1~360도 |
proximity_alert_radius | Integer | 선택 | 접근 알림 반경(미터). 0 이면 끕니다 |
live_period | Integer | 선택 | 새 공유 기간: 메시지를 보낸 시각부터 센 초. 지금 기간보다 하루 넘게 늘릴 수는 없고, 2147483647 이면 멈출 때까지 |
reply_markup | InlineKeyboardMarkup | 선택 | 새 버튼 |
inline_message_id | String | 안 됨 | 인라인 모드가 없어 400 |
돌려주는 것: 고친 Message
이미 끝난 실시간 위치 — 멈췄거나 기간이 다 된 것 — 는 옮길 수도, 기간을 늘려 되살릴 수도 없습니다(400 Bad Request: message can't be edited). 실시간 위치가 아닌 메시지도 마찬가지입니다.
마지막으로 반영된 이동에서 10초가 지나지 않았거나 10m 도 움직이지 않은 이동은 서버가 저장하지 않고 넘깁니다. 멈추기와 기간·알림 반경의 변경은 언제나 반영됩니다. 넘긴 경우 돌려주는 Message 는 마지막으로 반영된 위치를 보여 주므로, 몇 초마다 보내도 괜찮습니다.
stopMessageLiveLocation
POST/bot<토큰>/stopMessageLiveLocation
파라미터는 chat_id, message_id, reply_markup(선택). 실시간 위치를 마지막 자리에서 멈춥니다. 공유 기간이 실제로 흐른 시간으로 줄어들어 모든 앱이 끝난 위치로 그립니다. 이미 멈췄거나 끝났으면 400 Bad Request: message is not modified: … 입니다. 돌려주는 것: 고친 Message.
editMessageText
POST/bot<토큰>/editMessageText
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
chat_id | Integer 또는 String | 예 | 메시지가 있는 대화 |
message_id | Integer | 예 | 고칠 메시지 (봇이 보낸 것) |
text | String | 예 | 새 글, 1~4,096자 |
reply_markup | InlineKeyboardMarkup | 선택 | 새 버튼. 빼면 버튼이 없어집니다(텔레그램과 같음) |
inline_message_id | String | 안 됨 | 인라인 모드가 없어 400 |
돌려주는 것: 고친 Message. 앱에는 바로 반영되고, 사람이 고친 메시지는 봇에게 edited_message 로 옵니다.
글과 버튼이 전과 똑같으면 400 Bad Request: message is not modified: …, 남의 메시지를 고치려 하면 400 Bad Request: message can't be edited 입니다.
editMessageCaption
POST/bot<토큰>/editMessageCaption
파라미터는 chat_id, message_id, caption(0~1,024자, 빼면 설명을 지움), reply_markup. 돌려주는 것: 고친 Message.
editMessageReplyMarkup
POST/bot<토큰>/editMessageReplyMarkup
글은 그대로 두고 버튼만 바꿉니다. 파라미터는 chat_id, message_id, reply_markup(빼면 버튼을 지움). 돌려주는 것: 고친 Message.
deleteMessage
POST/bot<토큰>/deleteMessage
파라미터는 chat_id, message_id. 메시지를 대화의 양쪽에서 지웁니다. 없는 메시지면 400 Bad Request: message to delete not found. 돌려주는 것: True.
answerCallbackQuery
POST/bot<토큰>/answerCallbackQuery
사람이 누른 버튼(CallbackQuery)에 답합니다. 10초 안에, 한 번만 답할 수 있습니다.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
callback_query_id | String | 예 | 업데이트의 callback_query.id |
text | String | 선택 | 화면 위에 잠깐 뜨는 문구 |
show_alert | Boolean | 선택 | true 면 확인 버튼이 있는 창으로 |
url | String | 선택 | 앱이 열 주소 |
cache_time | Integer | 선택 | 앱이 이 답을 기억할 초 |
돌려주는 것: True. 너무 늦었거나 이미 답했으면 400 Bad Request: query is too old and response timeout expired or query ID is invalid.
sendChatAction
POST/bot<토큰>/sendChatAction
호환을 위해 받고 True 를 돌려주지만, 아직 앱에 "입력 중…" 을 표시하지는 않습니다.
getChat
GET/bot<토큰>/getChat?chat_id=…
파라미터는 chat_id(숫자). 사람이면 그 사람, 그룹이면 봇이 멤버인 그룹만. 돌려주는 것: Chat. 없거나 볼 수 없으면 400 Bad Request: chat not found.
getFile
GET/bot<토큰>/getFile?file_id=…
받은 사진의 file_id 로 내려받을 경로를 얻습니다. 돌려주는 것: File. 그다음 이 주소로 내려받습니다.
https://pabal.me/file/bot<토큰>/<file_path>
file_id 는 그 사진을 가져갈 권리이기도 합니다. 잘못된 값이면 400 Bad Request: wrong file identifier/HTTP URL specified.
setMyCommands
POST/bot<토큰>/setMyCommands
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
commands | Array of BotCommand | 예 | 최대 100개 |
language_code | String | 선택 | 언어별 목록으로 저장. 앱에는 지금 언어 코드 없는 기본 목록만 보입니다 |
scope | BotCommandScope | 무시 |
돌려주는 것: True. 규칙에 맞지 않으면 아무것도 저장하지 않고 400 Bad Request: BOT_COMMAND_INVALID(명령어), BOT_COMMAND_DESCRIPTION_INVALID(빈 설명이나 256자 초과), BOT_COMMANDS_TOO_MUCH(100개 초과).
getMyCommands
GET/bot<토큰>/getMyCommands
파라미터는 language_code(선택). 돌려주는 것: Array of BotCommand.
deleteMyCommands
POST/bot<토큰>/deleteMyCommands
파라미터는 language_code(선택). 그 목록을 비웁니다. 돌려주는 것: True.
logOut · close
텔레그램에서는 로컬 Bot API 서버로 옮길 때 쓰는 메서드입니다. 파발에는 옮길 곳이 없으므로 받기만 하고 True 를 돌려줍니다. 토큰을 무효로 하려면 BotFather 의 /revoke 를 쓰세요.
객체
필드 옆 선택 은 없을 수도 있다는 뜻입니다. 여기 없는 텔레그램의 다른 필드는 파발이 보내지 않습니다.
Update
새 소식 하나. update_id 와 아래 가운데 하나가 들어 있습니다.
| 필드 | 타입 | 설명 |
|---|---|---|
update_id | Integer | 1씩 커지는 번호. getUpdates 의 offset 에 씁니다 |
message 선택 | Message | 봇에게 온 새 메시지 (1:1, 또는 봇이 멤버인 그룹의 모든 메시지) |
edited_message 선택 | Message | 고쳐진 메시지 |
callback_query 선택 | CallbackQuery | 인라인 버튼 누름 |
voice_call 선택 | VoiceCall | 음성 봇에 온 통화와 봇이 건 전화의 시작·끝 (이름으로 청해야 옴) |
voice_call_tool_query 선택 | VoiceCallToolQuery | 통화 중 모델이 부른 봇의 도구 (이름으로 청해야 옴) |
voice_call_transcript 선택 | VoiceCallTranscript | 통화 중 대화 기록의 한 마디 (이름으로 청해야 옴) |
User
| 필드 | 타입 | 설명 |
|---|---|---|
id | Integer | 사용자 ID (이 서버 안에서만 뜻이 있음) |
is_bot | Boolean | 봇이면 true |
first_name | String | 이름. 지워진 계정이면 Deleted Account |
last_name 선택 | String | 성 |
username 선택 | String | 사용자명 (@ 없이) |
Chat
| 필드 | 타입 | 설명 |
|---|---|---|
id | Integer | 사람이면 그 사람의 ID(양수), 기본 그룹이면 음수 |
type | String | private 또는 group |
title 선택 | String | 그룹 이름 (group) |
first_name, last_name, username 선택 | String | 상대의 이름·사용자명 (private) |
Message
| 필드 | 타입 | 설명 |
|---|---|---|
message_id | Integer | 이 대화 안의 메시지 번호 |
from 선택 | User | 보낸 사람 |
chat | Chat | 메시지가 있는 대화 |
date | Integer | 보낸 시각 (유닉스 초) |
edit_date 선택 | Integer | 마지막으로 고친 시각 |
text 선택 | String | 글 (사진·파일·위치·장소·연락처 메시지가 아니면 늘 있음) |
entities 선택 | Array of MessageEntity | 글 속의 명령·멘션·URL·해시태그 |
photo 선택 | Array of PhotoSize | 사진 (파발은 원본 하나) |
document 선택 | Document | 파일 |
location 선택 | Location | 위치나 실시간 위치. 장소 메시지에도 그 위치가 함께 옵니다 |
venue 선택 | Venue | 장소 |
contact 선택 | Contact | 연락처 카드 |
caption 선택 | String | 사진 설명 |
caption_entities 선택 | Array of MessageEntity | 설명 속의 명령·멘션·URL·해시태그 |
reply_markup 선택 | InlineKeyboardMarkup | 메시지에 달린 인라인 버튼 |
MessageEntity
| 필드 | 타입 | 설명 |
|---|---|---|
type | String | bot_command, mention, url, hashtag |
offset | Integer | 시작 위치 (UTF-16 코드 단위) |
length | Integer | 길이 (UTF-16 코드 단위) |
서버가 글에서 자동으로 찾아 붙입니다. 굵게·기울임 같은 서식 엔티티는 아직 없습니다.
PhotoSize
| 필드 | 타입 | 설명 |
|---|---|---|
file_id | String | 내려받기(getFile)와 다시 보내기(sendPhoto)에 쓰는 ID |
file_unique_id | String | 같은 사진이면 봇이 달라도 같은 값. 내려받기에는 못 씀 |
width, height | Integer | 픽셀 크기 |
file_size | Integer | 바이트 |
Document
| 필드 | 타입 | 설명 |
|---|---|---|
file_id | String | 내려받기(getFile)와 다시 보내기(sendDocument)에 쓰는 ID |
file_unique_id | String | 같은 파일이면 봇이 달라도 같은 값. 내려받기에는 못 씀 |
file_name | String | 보낸 쪽이 준 이름 |
mime_type | String | 보낸 쪽이 말한 형식 |
file_size | Integer | 바이트 |
Location
| 필드 | 타입 | 설명 |
|---|---|---|
latitude, longitude | Float | 위도와 경도 |
horizontal_accuracy 선택 | Float | 오차 반경(미터) |
live_period 선택 | Integer | 실시간 위치의 공유 기간: 메시지를 보낸 시각부터 센 초. 멈추면 실제로 흐른 시간으로 줄어듭니다 |
heading 선택 | Integer | 진행 방향, 1~360도 |
proximity_alert_radius 선택 | Integer | 접근 알림 반경(미터) |
사람이 공유하는 실시간 위치는 서버가 반영할 때마다 edited_message 로 다시 옵니다.
Venue
| 필드 | 타입 | 설명 |
|---|---|---|
location | Location | 장소의 위치 |
title | String | 장소 이름 |
address | String | 주소 |
foursquare_id, foursquare_type 선택 | String | Foursquare 의 장소 ID 와 종류 |
google_place_id, google_place_type 선택 | String | Google 장소 ID 와 종류 |
Contact
| 필드 | 타입 | 설명 |
|---|---|---|
phone_number | String | 보낸 쪽이 적은 그대로의 번호 |
first_name | String | 이름 |
last_name 선택 | String | 성 |
user_id 선택 | Integer | 그 번호의 파발 계정. 보낸 사람이 자기 연락처에 저장해 둔 사람이거나 자기 자신일 때만 붙습니다 |
vcard 선택 | String | vCard 형식의 추가 정보 |
File
| 필드 | 타입 | 설명 |
|---|---|---|
file_id, file_unique_id | String | PhotoSize 와 같음 |
file_size | Integer | 바이트 |
file_path | String | photos/<file_id>.jpg 또는 documents/<file_id> 꼴. /file/bot<토큰>/ 뒤에 붙여 내려받습니다 |
CallbackQuery
| 필드 | 타입 | 설명 |
|---|---|---|
id | String | answerCallbackQuery 에 줄 ID |
from | User | 버튼을 누른 사람 |
message 선택 | Message | 버튼이 달린 메시지 |
chat_instance | String | 그 대화를 나타내는 값 |
data 선택 | String | 버튼의 callback_data |
InlineKeyboardMarkup
inline_keyboard: Array of Array of InlineKeyboardButton — 바깥 배열이 줄, 안쪽 배열이 한 줄의 버튼입니다. 한 메시지에 최대 100줄·100개.
InlineKeyboardButton
| 필드 | 타입 | 설명 |
|---|---|---|
text | String | 버튼 글자 |
callback_data 둘 중 하나 | String | 누르면 봇에게 가는 값, 1~64바이트 |
url 둘 중 하나 | String | 누르면 열 주소 |
switch_inline_query, web_app, login_url, pay 같은 다른 종류는 아직 없습니다(400).
ReplyKeyboardMarkup
| 필드 | 타입 | 설명 |
|---|---|---|
keyboard | Array of Array of KeyboardButton | 입력창 아래 버튼 판 |
resize_keyboard, one_time_keyboard, is_persistent, selective 선택 | Boolean | 크기 맞춤 · 한 번 쓰면 숨김 · 늘 보임 · 특정 사람에게만 |
input_field_placeholder 선택 | String | 입력창 안내 글자 |
KeyboardButton
문자열 하나, 또는 text 와 선택 필드 request_contact(내 연락처 보내기)·request_location(내 위치 보내기, Boolean)를 가진 객체.
ReplyKeyboardRemove
{"remove_keyboard": true} — 버튼 판을 숨깁니다. selective 선택.
ForceReply
{"force_reply": true} — 앱이 이 메시지에 답장하는 상태로 입력창을 엽니다. selective, input_field_placeholder 선택.
BotCommand
| 필드 | 타입 | 설명 |
|---|---|---|
command | String | 영소문자·숫자·밑줄 1~32자 (/ 없이) |
description | String | 1~256자 |
WebhookInfo
| 필드 | 타입 | 설명 |
|---|---|---|
url | String | 웹훅 주소, 없으면 빈 문자열 |
has_custom_certificate | Boolean | 늘 false |
pending_update_count | Integer | 전달을 기다리는 업데이트 수 |
ip_address 선택 | String | 마지막으로 보낸 IP |
last_error_date 선택 | Integer | 마지막 실패 시각 (유닉스 초) |
last_error_message 선택 | String | 마지막 실패 이유 (목록) |
max_connections 선택 | Integer | setWebhook 에 준 값 |
allowed_updates 선택 | Array of String | setWebhook 에 준 값 |
음성 통화
파발에만 있는 메서드입니다. 음성 봇은 사람이 앱에서 전화를 걸면 AI 음성 모델이 받는 봇이고, 이 메서드들로 그 모델의 설정을 정하고 통화를 다룹니다. 무엇이 켜져 있어야 하는지, 켜는 법과 예제는 음성 봇 안내에 있습니다. 메서드는 음성이 꺼진 서버에도 있어 설정을 읽을 수 있지만, 음성이 꺼졌거나 운영자가 아직 허용하지 않은 봇은 끄기와 지우기 말고는 설정을 바꿀 수 없습니다(403 VOICE_NOT_AVAILABLE).
setVoiceCallSettings
POST/bot<토큰>/setVoiceCallSettings
봇의 음성 설정을 바꿉니다. 보낸 것만 바뀌고, 전체를 검사한 뒤 한 번에 쓰거나 전부 거절합니다. 봇마다 한 시간 30번(@BotFather 와 합쳐서).
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
enabled | Boolean | 선택 | 켜기는 @BotFather 에서 음성 봇 규칙에 동의한 뒤에만(403 VOICE_RULES_NOT_ACCEPTED). 끄기는 언제나 |
instructions | String | 선택 | 모델의 지시문, 8,192바이트까지. "" 는 지움(켜진 관리형 봇은 안 됨) |
greeting | String | 선택 | 첫 인사, 512바이트까지. "" 면 모델이 알아서 |
voice | String | 선택 | 목소리 id, 기본 kore (목록) |
language | String | 선택 | ko-KR(기본) · en-US · ja-JP · cmn-CN · fr-FR · es-US · id-ID · vi-VN · ar-EG (목록). 대소문자를 가리지 않고, zh-CN 은 cmn-CN 으로 저장합니다. 한국어·영어가 아닌 봇은 운영자가 지시문·인사말을 읽고 허용한 뒤에 전화를 받습니다 |
max_call_duration | Integer | 선택 | 초, 60 ~ limits.max_call_duration |
max_concurrent_calls | Integer | 선택 | 1 ~ limits.max_concurrent_calls |
tools | Array of VoiceCallTool | 선택 | 통화 중 모델이 부를 봇의 도구: {"name", "description", "parameters"?, "timeout_ms"?}. 규칙은 도구 정하기. 폼에서는 JSON 문자열 |
transcript | Boolean | 선택 | 대화 기록 스위치. 켜기는 봇 주인 약관 뒤에만 |
mode | String | 선택 | managed(파발의 AI 모델) 또는 stream(원음 모드) |
stream_url, stream_secret | String | 선택 | mode=stream 과 함께. 비밀은 요청 본문으로만 |
돌려주는 것: True
getVoiceCallSettings
GET/bot<토큰>/getVoiceCallSettings
파라미터 없음. 지금 설정과, 봇이 지금 전화를 받을 수 있는지(available, unavailable_reason), 서버의 한도, 목소리와 언어 목록, 도구 질의·대화 기록이 갈 곳, 자기 키·원음 모드·MCP 서버의 상태. 키와 비밀은 나오지 않고 지문만 나옵니다. 필드는 음성 봇 — 지금 설정 보기에 있습니다.
돌려주는 것: VoiceCallSettings (객체)
endVoiceCall
POST/bot<토큰>/endVoiceCall
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
voice_call_id | String | 예 | voice_call.id |
farewell | Boolean | 선택 | 기본 true: 모델이 짧게 작별 인사를 하고 끊음(10초 안에 안 끊기면 서버가 끝냄). false 면 바로 |
돌려주는 것: True. 이 봇의 살아 있는 통화가 아니면 400 VOICE_CALL_NOT_FOUND.
sendVoiceCallContext
POST/bot<토큰>/sendVoiceCallContext
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
voice_call_id | String | 예 | 켜져 있는 통화 |
text | String | 예 | 모델에게 주는 봇 주인의 메모. 2,000자·6,144바이트까지, 내용 규칙 검사 |
respond | Boolean | 선택 | 기본 false. true 면 모델이 곧바로 반응 |
돌려주는 것: True. 연결 전 VOICE_CALL_NOT_ACTIVE, 원음 통화 VOICE_CONTEXT_NOT_SUPPORTED, 너무 김 VOICE_CONTEXT_TOO_LONG, 통화마다 1분에 10개를 넘으면 429.
getVoiceCallUsage
GET/bot<토큰>/getVoiceCallUsage
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
period | String | 선택 | today(기본) 또는 month |
month | String | 선택 | YYYY-MM, period=month 와 함께만 |
돌려주는 것: period, from_date, to_date, calls, seconds, missed, refused(busy·budget·unavailable), platform_seconds, own_key_seconds, remaining_seconds_today(bot·owner), 발신을 켠 적이 있는 봇이면 outbound(today·limits) — 날짜 경계는 서버의 시간대
startVoiceCall
POST/bot<토큰>/startVoiceCall
봇이 사람에게 전화를 겁니다. 봇 주인이 @BotFather 에서 켜고 운영자가 검토한 봇이, 그 봇에게 전화를 허락한 사람에게만 걸 수 있습니다. 답과 판정은 음성 봇 — 걸기에 있습니다.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
user_id | Integer | 예 | 걸 사람 |
purpose | String | 예 | reminder, wake_up, delivery, appointment 가운데 봇이 고른 것 |
context | String | 예 | 모델에게만 주는 안내, 1,000자·3,072바이트까지, 내용 규칙 검사 |
max_duration | Integer | 선택 | 받은 뒤 최대 초, 30 ~ 300(봇의 상한까지). 기본 180 |
ring_timeout | Integer | 선택 | 울리는 초, 30 ~ 45. 기본 40 |
돌려주는 것: {"voice_call_id": "…"} — 받아들였다는 뜻일 뿐이고, 울렸는지·받았는지는 voice_call 업데이트로 옵니다. 거절은 403 VOICE_OUTBOUND_OFF: <까닭>, 403 USER_UNAVAILABLE(늘 같은 몸), 429, 503 VOICE_UNAVAILABLE. 웹훅 응답으로는 부를 수 없습니다.
answerVoiceCallToolQuery
POST/bot<토큰>/answerVoiceCallToolQuery
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
tool_query_id | String | 예 | VoiceCallToolQuery 의 id |
result | String · Object · Array | 둘 중 하나 | 도구의 결과, 16,384바이트까지 |
error | String | 둘 중 하나 | 봇의 실패 문구, 512바이트까지 |
돌려주는 것: True. 첫 답이 마지막입니다. 늦으면 400 TOOL_QUERY_EXPIRED: answered|timeout|cancelled|call_ended|unreachable, 모르는 id 는 TOOL_QUERY_ID_INVALID, 답이 거절되면 TOOL_RESULT_EMPTY·TOOL_RESULT_INVALID·TOOL_RESULT_TOO_LONG·TOOL_ERROR_TOO_LONG·TOOL_RESULT_REFUSED: <코드>. 웹훅의 도구 질의 POST 에는 응답 본문으로도 답할 수 있습니다.
setVoiceCallApiKey · deleteVoiceCallApiKey
POST/bot<토큰>/setVoiceCallApiKey
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
provider | String | 예 | google(Vertex AI 서비스 계정 JSON 키) 또는 openai(sk-…) |
api_key | String | 예 | 키. 요청 본문으로만(쿼리면 400), 8KiB 까지 |
돌려주는 것: True — 게이트웨이의 시험 세션을 통과한 뒤에만 보관합니다. API_KEY_INVALID, API_KEY_NO_REALTIME_ACCESS, 503 API_KEY_CHECK_UNAVAILABLE(보관하지 않음), 403 VOICE_OWN_KEY_NOT_AVAILABLE(이 서버는 자기 키를 받지 않음). deleteVoiceCallApiKey 는 파라미터 없이 언제나 True. 두 메서드 모두 bots.sendCustomRequest 로는 부를 수 없고, setVoiceCallApiKey 는 웹훅 응답으로도 부를 수 없습니다. 자세한 것은 자기 모델 키.
MCP 서버 메서드
| 메서드 | 파라미터 | 돌려주는 것 |
|---|---|---|
setVoiceCallMcpServer | label, url, auth(객체), 선택 audience, allowed_tools, requested_level, timeout_ms, tools(분류 덧씌우기), declaration, auto_repin | 등록 결과(status, job_id). 받는 곳이 바뀌면 400 MCP_CHANGE_NEEDS_CONFIRMATION 뒤 @BotFather 에서 확인 |
refreshVoiceCallMcpServer | label | 다시 검사(refreshing: true, job_id) |
deleteVoiceCallMcpServer | label | True — MCP 가 꺼져 있어도 됨 |
getVoiceCallMcpBinding | 없음 | iss, sub(bot:<id>), issuer_document, jwks_uri, audiences |
createVoiceCallMcpClientKey | label | 공개 jwk, thumbprint (개인 키는 파발 밖으로 나오지 않음) |
auth 와 비밀은 요청 본문으로만 보냅니다. MCP 가 꺼진 서버에서는 403 VOICE_NOT_AVAILABLE: MCP tools are not available on this server. 자세한 것은 음성 봇 — MCP 서버.
음성 업데이트를 받으려면
voice_call, voice_call_tool_query, voice_call_transcript 는 이름으로 청해야만 옵니다 — getUpdates 나 setWebhook 의 allowed_updates 에 적으세요. 빈 목록으로는 오지 않고, 청하기 전에 일어난 것은 쌓이지 않습니다. 도구 질의는 웹훅이 있으면 따로 보내는 POST 로, 최근 15초 안에 getUpdates 를 불렀으면 다음 getUpdates 의 맨 앞에 옵니다.
VoiceCall
| 필드 | 타입 | 설명 |
|---|---|---|
id | String | 통화 id (부호 없는 10진수) |
direction | String | incoming(봇에게 온 전화) 또는 outgoing(봇이 건 전화). 늘 있음 |
event | String | started, ended, missed, refused, 봇이 건 전화는 started, ended, not_answered, failed |
from 선택 | User | 봇에게 온 전화만 — 발신자: id, is_bot, first_name, last_name, language_code. 사용자명과 전화번호는 없음 |
to 선택 | Object | 봇이 건 전화만 — {"id": …}, 받는 사람의 id 하나 |
purpose 선택 | String | 봇이 건 전화만 — 요청의 목적 |
date | Integer | 일어난 시각 (유닉스 초). started 는 앱의 통화면 키 교환이 끝났을 때, 애플 워치의 통화면 워치의 소리가 연결됐을 때. ended 는 대화 기록을 기다리거나 대화 남기기가 켜진 서버면 최대 10초 늦게 옵니다. 봇이 건 전화는 서버 설정과 상관없이, 받은 통화의 ended 가 게이트웨이의 사용량을 기다리느라 최대 10초 늦게 옵니다 |
max_duration 선택 | Integer | 이 통화의 최대 길이(초) — started, ended |
start_date, duration 선택 | Integer | 켜진 시각과 켜져 있던 초(서버 시계) — ended |
end_reason 선택 | String | caller(봇이 건 전화는 callee), bot, max_length, failed, operator, suspended — ended, missed |
refused_reason 선택 | String | busy, budget, unavailable — refused |
transcript 선택 | Object | {"turns": [{"role", "text", "offset_ms"}], "truncated"} — ended, 대화 기록이 켜졌을 때. 봇이 건 전화에는 없음 |
VoiceCall 에는 발신자가 대화 남기기를 골랐는지 알려 주는 필드가 없습니다. 의도한 것입니다 — 그 글은 발신자 자신의 것이고 봇에게 가지 않습니다. 그래서 대화 남기기가 켜진 서버에서는 원음 모드가 아닌 모든 통화의 ended 가 같은 만큼 기다리고, 통화 기록 다음 메시지 번호도 모든 통화에서 기록보다 31 이상 큽니다(번호를 미리 잡아 둠).
VoiceCallToolQuery
| 필드 | 타입 | 설명 |
|---|---|---|
id | String | answerVoiceCallToolQuery 에 줄 id |
voice_call_id | String | 통화 id |
from | User | 발신자 (VoiceCall 과 같은 필드) |
name | String | 도구 이름 |
arguments | String | 인자, JSON 객체의 텍스트(서버가 검사함) |
budget_ms | Integer | 받은 때부터 답이 서버에 닿아야 할 때까지의 밀리초 |
VoiceCallTranscript
| 필드 | 타입 | 설명 |
|---|---|---|
voice_call_id | String | 통화 id |
seq | Integer | 마디 번호 |
role | String | caller 또는 bot |
text | String | AI 가 적은 글. 카드·주민등록번호가 들면 숫자를 * 로 |
is_final | Boolean | 늘 true |
offset_ms, date | Integer | 통화 소리가 시작된 때부터의 밀리초, 시각 |
HTTP Bot API 로만 옵니다. 통화 전체는 끝난 voice_call 의 transcript 에 있습니다.
원음 모드 (mode=stream)
원음 모드에서는 통화의 소리가 파발의 AI 모델 대신 봇 주인의 서버로 갑니다. setVoiceCallSettings 에 mode=stream, stream_url=wss://…, stream_secret=…(32~128자 출력 가능한 ASCII, 요청 본문으로만)을 보내면, 파발의 게이트웨이가 그 주소에 한 번 접속해 보고(사전 시험) 성공하면 켜집니다. 봇 주인 약관과, 원음 확인 창이 든 앱 빌드가 필요합니다. 설정·사전 시험 결과·예제는 음성 봇 — 원음 모드에 있습니다.
- 연결: TLS 1.2 이상, URL 의 이름에 맞는 공인 인증서, 서브프로토콜
pabal.voice.v1, 압축 없음, 한 메시지 64KiB·글 4KiB 까지, 리다이렉트 없음. - 서명:
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>").host는 내가 알린 URL 의 호스트. 300초·nonce 한 번·바이트 비교·start.call_id가call과 같은지 확인(자세히). - 소리: 글 메시지는 JSON 이벤트, 바이너리는 PCM16 little-endian·모노·24 kHz. 발신자 소리는 20 ms(960바이트) 조각.
- 파발 → 봇:
start(한 번) ·live(AI 안내가 끝남) · 소리 ·mark·cleared·buffer_full·stop(→ 닫기 1000). - 봇 → 파발:
ready(start뒤 3초 안, 맨 먼저) · 소리(live뒤) ·clear·mark·hangup.
메시지 하나하나의 모양은 음성 봇 — 규격 표에 있습니다.
음성 메서드의 오류
음성 메서드의 오류 코드와 뜻은 음성 봇 — 오류에 모아 두었습니다. 403 은 Forbidden: <코드>: <설명> 꼴이고(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 만은 설명 없이 늘 Forbidden: USER_UNAVAILABLE 입니다. 한 시간 한도를 넘으면 429 와 retry_after 입니다.
텔레그램 Bot API 와 다른 점
- 업데이트 종류:
message,edited_message,callback_query와, 파발에만 있는 음성 봇 종류(voice_call,voice_call_tool_query,voice_call_transcript)만 옵니다. 채널 글, 인라인 쿼리, 결제, 투표, 멤버 변경(my_chat_member) 등은 없습니다. - 서식:
parse_mode·entities를 무시하고 글자 그대로 보냅니다. 명령·멘션·URL·해시태그만 자동으로 표시됩니다. - 대화 종류: 1:1 과 기본 그룹만. 채널·슈퍼그룹·포럼 토픽은 없습니다.
- 미디어: 사진, 파일(
sendDocument), 위치·실시간 위치·장소·연락처만. 동영상·음성으로 재생되는 메시지, 스티커, 앨범(sendMediaGroup), 투표, URL 로 보내기는 없습니다. HTTP 로 올리고 내려받는 것은 20MB 까지입니다. - 위치: 실시간 위치의 접근 알림(
proximity_alert_radius)은 저장만 하고 보내지 않습니다. 10초·10m 안쪽의 이동은 반영하지 않고 넘기며, 봇이 보낸 연락처 카드에는user_id가 붙지 않습니다. - 그룹의 봇: 프라이버시 모드가 없어 그룹의 모든 메시지를 받습니다.
- 대기열: 가져가지 않은 업데이트는 봇마다 최근 1,000개까지 메모리에 두며, 서버 재시작 때 사라집니다(텔레그램은 24시간 보관).
- 웹훅: 한 번에 하나씩 순서대로 보내고(
max_connections는 보관만), 자체 서명 인증서를 받지 않으며, 포트 제한이 없습니다. - ID: 사용자·메시지·파일 ID 는 이 서버 안에서만 뜻이 있습니다. 채널·슈퍼그룹이 없어
-100…꼴의 ID 도 없습니다. - 없는 메서드: 위 목록에 없는 것(
forwardMessage,copyMessage,sendVideo,sendPoll,getChatMember,banChatMember,answerInlineQuery…)은404 Not Found: method not found.