Send website form leads to Telegram (and the mistakes that leak your bot token)

# telegram# webdev# javascript# tutorial
Send website form leads to Telegram (and the mistakes that leak your bot token)Sitecog

Leads that land in Telegram get answered faster than leads that land in an inbox: the whole team sees...

Leads that land in Telegram get answered faster than leads that land in an inbox: the whole team sees them in a group chat, on their phones, within seconds. Setting this up takes about 20 minutes, but there are a few traps — the most common one puts your bot token in front of every visitor.

Below: creating the bot, finding the chat id, a small Node.js handler, the mistakes I see most often, and an option for when you don't want to run a backend for forms at all.

1. Create a bot

  1. Open @BotFather in Telegram and send /newbot.
  2. Pick a name and a username ending in bot, e.g. myshop_leads_bot.
  3. BotFather replies with a token like 123456789:AA.... Treat it like a password.
  4. Open your bot and press Start. A Telegram bot can't message a person first — skip this step and nothing will arrive.

Tip: create a group, add the bot and your teammates, and send leads there. Everyone sees the lead, and everyone sees who picked it up.

2. Find the chat id

  1. Send any message to the bot (or to the group with the bot in it).
  2. Open https://api.telegram.org/bot<TOKEN>/getUpdates in your browser.
  3. Find "chat":{"id":...} in the response. Private chats have a positive id, groups a negative one (big groups start with -100).

If you get an error about a webhook instead, the bot already has one set, and getUpdates doesn't work alongside it. Remove it with deleteWebhook or create a separate bot for leads.

3. A small Node.js handler

The form posts to your server; the server talks to Telegram. The token lives in an environment variable, never in the page.

// server.js — Node 18+, no dependencies besides express
import express from 'express';

const app = express();
app.use(express.urlencoded({ extended: false }));

const { TG_BOT_TOKEN, TG_CHAT_ID } = process.env;

app.post('/lead', async (req, res) => {
  // Honeypot: hidden from people, bots fill it in
  if (req.body.website) return res.sendStatus(204);

  const name = String(req.body.name ?? '').trim();
  const phone = String(req.body.phone ?? '').trim();
  const message = String(req.body.message ?? '').trim();
  if (!phone) return res.status(400).send('Please add a phone number');

  // Plain text, no parse_mode: nothing to escape
  const text = `New lead\nName: ${name}\nPhone: ${phone}\n\n${message}`.slice(0, 4000);

  const r = await fetch(`https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ chat_id: TG_CHAT_ID, text }),
  }).catch(() => null);

  const ok = r && (await r.json()).ok;
  res.status(ok ? 200 : 502).send(ok ? 'Thanks! We will get back to you soon.' : 'Could not send — please call us.');
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode
<form action="/lead" method="post">
  <input name="name" placeholder="Name">
  <input name="phone" type="tel" placeholder="Phone" required>
  <textarea name="message" placeholder="Message"></textarea>
  <!-- honeypot: hidden from people -->
  <input name="website" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">
  <button>Send</button>
</form>
Enter fullscreen mode Exit fullscreen mode

4. Mistakes I see most often

  • The token in front-end JavaScript. "Just call the Bot API from the browser" means every visitor can read the token and use your bot to spam or read its updates. Sending must happen on a server.
  • No spam protection. A form that forwards everything to Telegram will be forwarding ads within a week. At minimum: a honeypot field and a rate limit per IP.
  • parse_mode: 'HTML' with raw user input. A single < in a visitor's message makes Telegram reject the whole request. Send plain text or escape the input.
  • Messages over 4096 characters. Telegram rejects them. Truncate.
  • Telegram as the only copy. If the request fails, the lead is gone. Store it in a database or send it by email as well.

5. Without a backend

If your site is custom-built and you'd rather not write and maintain a handler, a service can take the form submission for you. Disclosure: I work on Sitecog, which does exactly this — so here is how it works there, and its limits.

You add one script and an attribute to the form you already have:

<script src="https://widget.sitecog.com/widget.js" defer></script>

<form data-crm-lead="callback">
  <input name="name" placeholder="Name">
  <input name="phone" type="tel" placeholder="Phone">
  <textarea name="message" placeholder="Message"></textarea>
  <button>Send</button>
</form>
Enter fullscreen mode Exit fullscreen mode

The widget catches the submit, saves the lead in the CRM and notifies your bot within a minute: form name, contact details, the start of the message and a link to the lead. Spam protection works without a captcha (a honeypot plus a one-time pass), and suspicious leads arrive marked as such. Recipients don't need chat ids — each one sends /start to your bot and you enable them in the settings; everyone has their own quiet hours. For React or Vue there's window.crmLead().

Limits, honestly: lead forms are on the Pro plan ($79 per 30 days), there are no email notifications or webhooks yet, and you can't reply to the client from Telegram — Telegram only tells you about the lead. Docs: sitecog.com/docs/leads.

How to check it works

  1. Send a test lead from a phone and from a laptop.
  2. Make sure every recipient and the group got it.
  3. Submit with an empty phone — it must not go through.
  4. A week later, look at what arrived: spam, missing submissions, duplicates.

What do you use for form leads — Telegram, email, Slack? Curious what has held up best for you.