Botledge docs
Botledge runs your Telegram bot on Cloudflare's edge. Telegram pushes each update to your bot's webhook, your code runs, and your bot replies. You write plain JavaScript — nothing to install, no server to keep alive.
Getting started
Three steps:
- Open Telegram, message
@BotFather, run/newbot, copy the token. - In the dashboard, click + New bot, paste the token, write your code.
- Click ▶ Run. Botledge registers the webhook and your bot is live 24/7.
The bot API
Two ways to handle updates. Both receive a ctx object.
// Runs when someone sends /start
bot.command('start', async (ctx) => {
await ctx.reply('Hello!');
});
// Runs for every plain text message
bot.on('message', async (ctx) => {
await ctx.reply('You said: ' + ctx.text);
});
// Runs for button taps
bot.on('callback_query', async (ctx) => {
await ctx.answer('Got it!');
});
// Runs for every update, whatever the type
bot.on('update', async (ctx) => { /* ... */ });
Events you can listen to: message, callback_query, inline_query, update.
The ctx object
| Property | Description |
|---|---|
ctx.text | The message text. |
ctx.chatId | Chat id to reply to. |
ctx.from | The sender: { id, first_name, username, … }. |
ctx.userId | Sender's Telegram id (used for ctx.store). |
ctx.command | Command name (without /) when a command matched. |
ctx.args | Array of words after the command. |
ctx.update | The raw Telegram update. |
| Method | Description |
|---|---|
ctx.reply(text, opts?) | Send a message to the chat. opts is any Telegram sendMessage option, e.g. { parse_mode: 'HTML', reply_markup }. |
ctx.send(text, opts?) | Alias for ctx.reply. |
ctx.api(method, params) | Call any Telegram Bot API method directly, e.g. ctx.api('sendPhoto', { chat_id, photo }). |
ctx.answer(text?, opts?) | (callback_query) Answer the button tap. |
ctx.edit(text, opts?) | (callback_query) Edit the message the button was on. |
Storage — ctx.store
Every bot gets a small key/value store backed by SQLite (Cloudflare D1). It's automatically scoped to the user talking to the bot, so scores and settings stay per-user. Values are JSON.
bot.command('points', async (ctx) => {
const score = (await ctx.store.get('score')) || 0;
await ctx.store.set('score', score + 1);
await ctx.reply('Your score: ' + (score + 1));
});
await ctx.store.del('score'); // remove a key
Calling APIs
fetch is available. Call any HTTPS API from your bot:
bot.command('quote', async (ctx) => {
const res = await fetch('https://api.quotable.io/random');
const data = await res.json();
await ctx.reply(data.content);
});
What works & what errors
Your code runs on Cloudflare Workers — a JavaScript edge runtime. Write within those bounds:
✓ Supported
Async/await, promises, fetch(), JSON, dates, the bot API, ctx.store, and any HTTPS service.
✕ Rejected with an error
require(), process, fs, http, child_process, ES import, eval(), infinite loops, and long polling. Botledge catches these and tells you exactly what to change.
Full example — a counter bot
bot.command('start', async (ctx) => {
await ctx.reply(
'Hi ' + ctx.from.first_name + '!\\n' +
'Send /count to add a point, /score to check.'
);
});
bot.command('count', async (ctx) => {
const n = ((await ctx.store.get('n')) || 0) + 1;
await ctx.store.set('n', n);
await ctx.reply('Count is now ' + n);
});
bot.command('score', async (ctx) => {
const n = (await ctx.store.get('n')) || 0;
await ctx.reply('Your score: ' + n);
});
bot.on('message', async (ctx) => {
await ctx.reply('Unknown command. Try /start');
});
Troubleshooting
My bot doesn't reply. Open the dashboard, check the status pill says live, and read the Logs panel. Make sure you messaged the bot (not someone else's).
Deploy failed. Usually a bad token — copy it again from @BotFather. The token looks like 123456789:ABC….
Error in logs. The log line names the problem — e.g. a Node module you used, or a syntax error. Fix and hit Run again.
Changed my code, nothing happened. Hit Run after saving to re-deploy.
Botledge is free and open source. Built on Cloudflare Workers + D1 + Dynamic Workers.