Birdie API

HTTP reference for the surface your Birdie VM calls when it delivers messages back through @birdieagentbot. If you just want to chat with your assistant on Telegram, you don’t need this page.

Overview

Every paying Birdie user runs their own Hetzner VM with OpenClaw and the birdie-channel plugin installed. The plugin calls this API over HTTPS to send replies, media, reactions, edits, and inline keyboards back to your Telegram chat.

Where the key comes from

On first payment, the router provisions your VM and writes BIRDIE_API_KEY into /etc/default/openclaw on it. If you want to use the API from your own code on that VM, read it from there.

Quick start

curl https://birdie6.com/birdie-api/sendMessage \
  -H "Content-Type: application/json" \
  -H "X-Birdie-API-Key: birdie_xxxxxxxxxxxxxxxx" \
  -d '{"chat_id":"123456789","text":"Hello from my VM."}'
Response shape. Success is 200 OK with { "ok": true, "result": { ... } }. Errors return { "error": "..." } with a 400/401/403/500 status.

Conventions

Chat ID ownership

Every request body carries chat_id. The router rejects with 403 Chat ID mismatch if it doesn’t match the owner of the API key. There is no way to send to another user’s chat — this is deliberate.

Sending media

Media fields (photo, video, document, …) accept any of three forms:

{
  "chat_id": "123456789",
  "photo": "b64:iVBORw0KGgoAAAANSUhEUg...",
  "photo_filename": "chart.png",
  "photo_content_type": "image/png",
  "caption": "Today’s numbers"
}

Telegram options passthrough

Any extra fields in the body (parse_mode, reply_markup, reply_to_message_id, disable_web_page_preview, …) are forwarded to the Bot API unchanged. See the Telegram Bot API reference for the full list.

Endpoints

Send

POST

/birdie-api/sendMessage

Send a text message.

FieldTypeNotes
chat_idstringRequired. Your chat ID.
textstringRequired.
parse_modestringOptional. Markdown, MarkdownV2, or HTML.
reply_markupobjectOptional inline keyboard.
POST

/birdie-api/sendPhoto

Send a photo. photo is a URL, file_id, or b64: payload.

POST

/birdie-api/sendDocument

Send any file. Use for non-media attachments or anything over 10 MB.

POST

/birdie-api/sendAudio

Send an audio track (MP3/M4A). performer, title, duration optional.

POST

/birdie-api/sendVoice

Send a voice note (OGG/Opus). Renders as a waveform in Telegram.

POST

/birdie-api/sendVideo

Send a video (MP4). duration, width, height, supports_streaming optional.

POST

/birdie-api/sendAnimation

Send a GIF/MP4 animation without sound.

POST

/birdie-api/sendVideoNote

Send a round video note. Must be square MP4, typically ≤ 1 min.

POST

/birdie-api/sendSticker

Send a WebP/TGS sticker by file_id or URL.

POST

/birdie-api/sendLocation

Required: latitude, longitude. Optional: live_period.

POST

/birdie-api/sendVenue

Required: latitude, longitude, title, address.

POST

/birdie-api/sendContact

Required: phone_number, first_name. Optional: last_name.

POST

/birdie-api/sendDice

Send a dice/emoji animation. Optional emoji: 🎲 🎯 🏀 ⚽ 🎳 🎰.

POST

/birdie-api/sendPoll

Required: question, options (array of 2–10 strings).

POST

/birdie-api/sendMediaGroup

Album of 2–10 items. media is an array of {type, media, caption?}.

POST

/birdie-api/sendChatAction

Show typing/uploading indicators. action: typing, upload_photo, record_voice, …

Feedback

POST

/birdie-api/answerCallbackQuery

Respond to an inline-keyboard button press. Requires callback_query_id; optional text, show_alert, url.

Edit & delete

POST

/birdie-api/editMessageText

Required: text, plus either chat_id+message_id or inline_message_id.

POST

/birdie-api/editMessageCaption

Same targeting rules. Required: caption.

POST

/birdie-api/editMessageMedia

Swap the media of a previously sent message. Required: media object.

POST

/birdie-api/editMessageReplyMarkup

Change or remove an inline keyboard. Required: reply_markup.

POST

/birdie-api/deleteMessage

Required: chat_id, message_id.

Forward & copy

POST

/birdie-api/forwardMessage

Required: chat_id, from_chat_id, message_id. Both chat IDs must match the API key owner.

POST

/birdie-api/copyMessage

Same fields as forwardMessage. Copies without the “forwarded from” header.

Errors

StatusMeaning
401Missing or unrecognised X-Birdie-API-Key.
403chat_id doesn’t belong to the key’s owner.
400Malformed body or missing required field.
429Rate limit exceeded. Response carries a Retry-After header (seconds) and {"retry_after": N} in the body.
500Telegram rejected the call or the router hit an internal error. The error field carries the underlying message.

Rate limits

Each API key gets a token bucket: 60 requests per minute with a burst of 20. That’s enough for normal chat, a morning briefing, a cron digest, or a small media group. Beyond that you’ll start getting 429s — back off for the number of seconds in Retry-After and try again.

On top of that, Telegram’s own Bot API limits still apply (roughly 1 msg/sec per chat, 20/min for group bursts). You also share the @birdieagentbot identity with every other Birdie user — be kind.