MeetCal: Why I Built Calendly Inside Telegram
Every 'can we chat?' in Telegram ends with a Calendly link that yanks you into a browser. MeetCal keeps the whole scheduling loop inside one conversation.
There’s a specific moment that broke me.
Someone messages me in Telegram: “can we jump on a call this week?” I reply: “sure, here’s my Calendly.” They click it, the browser opens, loads slowly on mobile, maybe asks them to create an account, maybe times out. Five minutes later they’re back in Telegram confirming the slot. The whole conversation is now split across two apps and a browser tab.
This happens dozens of times a week. I built MeetCal because I got tired of that split.
The problem with Calendly in Telegram
Calendly is fine. It does what it says. But the product assumption baked into it is that scheduling is a standalone task — something you navigate to deliberately, like booking a flight.
Telegram scheduling doesn’t work like that. “Can we chat?” is a throwaway line in the middle of a conversation about something else. When you drop a Calendly link into that moment, you’re asking the other person to pause, switch contexts, complete a multi-step flow in a foreign interface, then come back. Half the time the momentum is gone by the time they return.
The friction isn’t just the extra steps. It’s the break in the conversational thread. The context switch signals “this is now a formal scheduling task” when all you wanted was to find a free hour.
What MeetCal does
The host sets up availability once in the bot. From that point, you can share your booking card inline — type @MeetCalBot in any Telegram chat and it inserts a rich preview card directly into the conversation. The guest taps it, a Mini App opens inside Telegram, they pick a slot, done. No browser, no email, no account creation.
Confirmation and reminders happen as Telegram messages. The entire loop — suggest, book, confirm, remind — stays in one place.
The inline sharing part matters more than it sounds. It means you don’t have to leave the chat to share your calendar. You just type the bot name mid-message and the card appears. The recipient never sees a raw link.
The hardest technical problem
The app is React + Vite. Telegram Mini Apps give you access to WebApp.MainButton, which is the button at the bottom of the screen that floats over your content. It’s the primary action button in almost every Mini App.
The API is imperative: MainButton.show(), MainButton.hide(), MainButton.setText('Book slot'), MainButton.showProgress(). You call methods, Telegram handles the rendering.
React is declarative. You describe state, React decides what to render.
The mismatch created a specific bug: every time component state changed — even state unrelated to the button — the button would flicker. It would disappear for a single frame before reappearing with the correct text. On a phone screen this looks broken.
The root cause was that I had a single useEffect watching all button-related state and calling show() then setText() on every render. The brief hide() between renders was visible.
The fix was splitting the hook into separate effects with separate dependency arrays. One effect runs only on mount to initialize the button and attach the click handler. A separate effect runs only when the button text or loading state changes, and it never calls hide() — only setText() and showProgress(). Visibility is managed independently with its own effect that only fires when the visible flag changes.
Three effects instead of one. Each with a minimal dependency array. The flickering stopped.
It’s a small thing but it took half a day to isolate, and it’s the kind of thing that makes or breaks whether an app feels native or janky.
What the Mini App platform gets right and wrong
Right: distribution is free. If the bot works, people find it through inline search. No app store review, no install step. The Telegram UI primitives — MainButton, BackButton, haptic feedback, the native share sheet — are enough to build something that feels like a real app.
Wrong: the debugging experience is rough. Errors in the Mini App show up in ways that are hard to trace. The WebApp object behaves differently across Telegram clients (desktop, iOS, Android) in ways that aren’t documented. I spent a day debugging a layout issue that only reproduced on Android because the viewport height calculation includes the keyboard on one platform and not the other.
The platform is young. You feel that. But the constraints push you toward simpler UIs, which is usually a good thing.
Current state
MeetCal has three tiers: free (basic booking, limited slots per month), Starter, and Pro. Paid tiers unlock recurring availability, custom buffers between meetings, and a few other things power users ask about.
Monetization is through Telegram Stars — Telegram’s in-app currency. It’s a natural fit because users never leave the platform to pay. The conversion from free to paid is early, but the people who pay tend to use the product seriously.
What I’d do differently
I’d spend less time on the booking flow polish in the first version and more time on the host onboarding. The moment where someone sets up their availability for the first time is where most people drop off. The flow works but it’s not obvious enough. I kept optimizing the guest experience — the person booking — when the host experience was the actual bottleneck.
Also: I’d have read the Telegram Mini Apps changelog more carefully before starting. There were API changes between when I started building and when I shipped that required rewriting parts of the auth layer. Lesson: treat the platform as unstable until you’ve shipped at least one version.
The core bet still holds. Messaging is where attention lives. Scheduling should live there too.