telegram-bot-onboarding Skill
First 30–60 seconds with a user: the /start hero + menu, /help, empty-state copy, follow-up sequences, and the Mini App graduation decision. For microcopy/error/loading UX rules, see telegram-bot-ux-rules. For flow patterns (wizard, branching, etc.), see telegram-bot-flow-patterns.
Built for the agntdev pipeline. See
agnt-cli-builder for the build loop.
1. Onboarding — first 30–60 seconds
Onboarding has a clock: segment or lose the user. After 60s of
unclear choice, drop-off is steep.
Step 1: /start sends a hero + 3–5 menu buttons
bot.command("start", async (ctx) => {
ctx.session.step = "menu";
await ctx.reply(
"👋 Welcome to Bookings.\n\n" +
"What brings you here?",
{
reply_markup: menuKeyboard([
{ text: "📅 Book a slot", data: "menu:book" },
{ text: "📋 My bookings", data: "menu:my" },
{ text: "❓ How it works", data: "menu:help" },
]),
},
);
});
Rules:
- Hero ≤ 6 lines. No walls of text.
- 3–5 buttons max. More than 5 = decision paralysis.
- Wording matches user intent. "Book a slot" not "Schedule resource reservation".
- Emoji to disambiguate, not decorate.
- One primary CTA (📅 Book) + one secondary (📋 My bookings) + one help escape (❓ How it works).
Step 2: Branch into the chosen intent
Use Flow Pattern B (Branching menu) from
telegram-bot-flow-patterns.
Step 3: /help always exists
bot.command("help", async (ctx) => {
await ctx.reply(
"📖 How to use this bot:\n\n" +
"• /start — Main menu\n" +
"• /book — Book a slot\n" +
"• /cancel — Cancel a pending flow\n" +
"• /my — View your bookings\n\n" +
"Need more help? Reply to this message.",
);
});
Step 4: Empty state for first-run features
If a feature has no data for this user, show an empty state, not a silent message.
bot.command("my", async (ctx) => {
const bookings = await db.getBookings(ctx.from.id);
if (bookings.length === 0) {
await ctx.reply(
"📋 You have no bookings yet.\n\n" +
"Tap 📅 Book to schedule one.",
{
reply_markup: inlineKeyboard([[
inlineButton("📅 Book now", "menu:book"),
]]),
},
);
return;
}
// ... render bookings list
});
Step 5: Easy exit + frequency caps
For follow-up sequences:
- Stop button always on automated messages.
- Frequency cap: ≤ 1 automated message per 12h per user unless they replied.
- Quiet hours: don't follow up at night in user's timezone.
Sequences and onboarding copy rules
- Easy exit always: include "Stop" button or instructions like "Reply STOP to pause".
- Frequency caps: no more than 1 automated message per 12 hours per user unless they replied.
- Quiet hours: don't follow up at night in the user's timezone.
- Light personalization: avoid repeated identical messages; vary slightly.
2. Mini App graduation — when to upgrade from inline keyboards
Inline keyboards hit a ceiling. Graduate to a Telegram Mini App
(embedded web view) when any of these thresholds is hit:
| Threshold |
Why inline keyboards fail |
| Option list >50 items with re-sorts >1/hour |
Client cache invalidates; pagination breaks. |
| Multi-select with Apply semantics |
No native checkbox in inline keyboards — you need a message per toggle. |
| Compliance audit trail required |
callback_data retained only 24h via getUpdates. Web App logs instantly to your store. |
| >4 KB payload per state |
Inline keyboard JSON explodes. Web App streams from your backend. |
When none of these apply, stay on inline keyboards — they're
cheaper to build, render faster, and don't require web hosting.
Decision
Need >50 items that change frequently?
├── Yes → Mini App
└── No → Inline keyboards
Multi-select with Apply semantics?
├── Yes → Mini App
└── No → Inline keyboards
Need full audit trail of user interactions?
├── Yes → Mini App
└── No → Inline keyboards
Anything else (≤50 items, simple choices, real-time feedback)?
└── Inline keyboards (default)
Web App button
The bridge between inline keyboards and Mini App:
inlineKeyboard([[
webAppButton("🛒 Open shop", "https://shop.example.com/twa"),
]])
User taps → Mini App opens → user interacts in the embedded web view →
app sends a message back via sendMessage on close. Use for: catalog
browse, settings panels, checkout, dashboards.
Quick Reference
| Concern |
Rule |
| /start hero |
≤ 6 lines, sentence case, 1 primary CTA + 1 secondary + 1 help |
| Buttons on /start |
3–5 max, verb-first, ≤24 chars |
| /help command |
Always exists; lists every command |
| Empty state |
Always show "No X yet — tap Y to start." |
| Follow-up frequency |
≤ 1 message per 12h unless user replied |
| Quiet hours |
No follow-ups overnight in user's TZ |
| Mini App trigger |
>50 items w/ re-sorts, multi-select Apply, audit trail, >4KB |
Cross-references
telegram-bot-flow-patterns — branching menu pattern, linear wizard, search-then-pick
telegram-bot-ui — menuKeyboard, inlineKeyboard, webAppButton
telegram-bot-ux-rules — microcopy, button labels, error/loading UX
telegram-bot-api-rich-messages — Rich Messages, Checklists
1---2name: telegram-bot-onboarding3description: Design a Telegram bot's first 30–60 seconds with a user. Covers /start hero and menu wiring, /help pattern, empty-state copy for first-run features, follow-up sequences (easy-exit, frequency caps, quiet hours), and the decision tree for graduating to a Telegram Mini App. USE FOR: onboarding, /start hero, first-run, empty state, /help, follow-up, frequency cap, quiet hours, Mini App graduation, web_app button, welcome flow, new user flow — even if the user doesn't say "onboarding" or "Mini App" explicitly. DO NOT USE FOR: button wiring (see telegram-bot-ui), flow patterns (see telegram-bot-flow-patterns), microcopy rules (see telegram-bot-ux-rules). Triggers: onboarding, /start hero, first-run, empty state, /help, follow-up, frequency cap, quiet hours, Mini App graduation, web_app button, welcome flow, new user flow.4license: MIT5---67# telegram-bot-onboarding Skill89First 30–60 seconds with a user: the /start hero + menu, /help, empty-state copy, follow-up sequences, and the Mini App graduation decision. For microcopy/error/loading UX rules, see [telegram-bot-ux-rules](../telegram-bot-ux-rules/SKILL.md). For flow patterns (wizard, branching, etc.), see [telegram-bot-flow-patterns](../telegram-bot-flow-patterns/SKILL.md).1011> **Built for the agntdev pipeline.** See12> [agnt-cli-builder](../agnt-cli-builder/SKILL.md) for the build loop.1314---1516## 1. Onboarding — first 30–60 seconds1718Onboarding has a **clock**: segment or lose the user. After 60s of19unclear choice, drop-off is steep.2021### Step 1: `/start` sends a hero + 3–5 menu buttons2223```ts24bot.command("start", async (ctx) => {25 ctx.session.step = "menu";26 await ctx.reply(27 "👋 Welcome to Bookings.\n\n" +28 "What brings you here?",29 {30 reply_markup: menuKeyboard([31 { text: "📅 Book a slot", data: "menu:book" },32 { text: "📋 My bookings", data: "menu:my" },33 { text: "❓ How it works", data: "menu:help" },34 ]),35 },36 );37});38```3940**Rules:**41- **Hero ≤ 6 lines.** No walls of text.42- **3–5 buttons max.** More than 5 = decision paralysis.43- **Wording matches user intent.** "Book a slot" not "Schedule resource reservation".44- **Emoji to disambiguate, not decorate.**45- **One primary CTA** (📅 Book) + one secondary (📋 My bookings) + one help escape (❓ How it works).4647### Step 2: Branch into the chosen intent4849Use Flow Pattern B (Branching menu) from50[telegram-bot-flow-patterns](../telegram-bot-flow-patterns/SKILL.md).5152### Step 3: `/help` always exists5354```ts55bot.command("help", async (ctx) => {56 await ctx.reply(57 "📖 How to use this bot:\n\n" +58 "• /start — Main menu\n" +59 "• /book — Book a slot\n" +60 "• /cancel — Cancel a pending flow\n" +61 "• /my — View your bookings\n\n" +62 "Need more help? Reply to this message.",63 );64});65```6667### Step 4: Empty state for first-run features6869If a feature has no data for this user, **show an empty state, not a silent message**.7071```ts72bot.command("my", async (ctx) => {73 const bookings = await db.getBookings(ctx.from.id);74 if (bookings.length === 0) {75 await ctx.reply(76 "📋 You have no bookings yet.\n\n" +77 "Tap 📅 Book to schedule one.",78 {79 reply_markup: inlineKeyboard([[80 inlineButton("📅 Book now", "menu:book"),81 ]]),82 },83 );84 return;85 }86 // ... render bookings list87});88```8990### Step 5: Easy exit + frequency caps9192For follow-up sequences:9394- **Stop button** always on automated messages.95- **Frequency cap**: ≤ 1 automated message per 12h per user unless they replied.96- **Quiet hours**: don't follow up at night in user's timezone.9798### Sequences and onboarding copy rules99100- **Easy exit** always: include "Stop" button or instructions like "Reply STOP to pause".101- **Frequency caps**: no more than 1 automated message per 12 hours per user unless they replied.102- **Quiet hours**: don't follow up at night in the user's timezone.103- **Light personalization**: avoid repeated identical messages; vary slightly.104105---106107## 2. Mini App graduation — when to upgrade from inline keyboards108109Inline keyboards hit a ceiling. Graduate to a **Telegram Mini App**110(embedded web view) when **any** of these thresholds is hit:111112| Threshold | Why inline keyboards fail |113|---|---|114| Option list **>50 items** with re-sorts >1/hour | Client cache invalidates; pagination breaks. |115| **Multi-select with Apply** semantics | No native checkbox in inline keyboards — you need a message per toggle. |116| **Compliance audit trail** required | `callback_data` retained only 24h via `getUpdates`. Web App logs instantly to your store. |117| **>4 KB payload** per state | Inline keyboard JSON explodes. Web App streams from your backend. |118119When **none** of these apply, **stay on inline keyboards** — they're120cheaper to build, render faster, and don't require web hosting.121122### Decision123124```125Need >50 items that change frequently?126├── Yes → Mini App127└── No → Inline keyboards128Multi-select with Apply semantics?129├── Yes → Mini App130└── No → Inline keyboards131Need full audit trail of user interactions?132├── Yes → Mini App133└── No → Inline keyboards134Anything else (≤50 items, simple choices, real-time feedback)?135└── Inline keyboards (default)136```137138### Web App button139140The bridge between inline keyboards and Mini App:141142```ts143inlineKeyboard([[144 webAppButton("🛒 Open shop", "https://shop.example.com/twa"),145]])146```147148User taps → Mini App opens → user interacts in the embedded web view →149app sends a message back via `sendMessage` on close. Use for: catalog150browse, settings panels, checkout, dashboards.151152---153154## Quick Reference155156| Concern | Rule |157|---|---|158| /start hero | ≤ 6 lines, sentence case, 1 primary CTA + 1 secondary + 1 help |159| Buttons on /start | 3–5 max, verb-first, ≤24 chars |160| /help command | Always exists; lists every command |161| Empty state | Always show "No X yet — tap Y to start." |162| Follow-up frequency | ≤ 1 message per 12h unless user replied |163| Quiet hours | No follow-ups overnight in user's TZ |164| Mini App trigger | >50 items w/ re-sorts, multi-select Apply, audit trail, >4KB |165166---167168## Cross-references169170- `telegram-bot-flow-patterns` — branching menu pattern, linear wizard, search-then-pick171- `telegram-bot-ui` — `menuKeyboard`, `inlineKeyboard`, `webAppButton`172- `telegram-bot-ux-rules` — microcopy, button labels, error/loading UX173- `telegram-bot-api-rich-messages` — Rich Messages, Checklists