REAL BUILD · AUTOMATION
How to Send Website Form Submissions to Telegram
Build a secure contact form that sends new website leads straight to Telegram — without exposing your bot token in the browser.
TRY THE WORKING VERSION
Send a test lead
This form uses the same architecture you'll build below. Your browser sends the form to How2Code's Django backend; the backend sends the Telegram message. The bot token never reaches the page.
New How2Code demo submission
Name: John Smith
Email: [email protected]
Message: I'd like a quote
The demo destination is fixed on the server. Visitors cannot provide a bot token, chat ID or arbitrary destination, so this endpoint cannot act as a general-purpose Telegram relay.
BUILD YOUR OWN VERSION · AI
Describe the form you actually need
Don't copy our contact form field for field. Tell How2Code what your business or workflow needs. AI will adapt this proven Website Form → Django → Telegram pattern and give you the files and setup steps.
YOUR BUILD
Form fields
The builder generates placeholders only. Never paste a real Telegram bot token or OpenAI API key into the description.
WHAT WE'RE BUILDING
A small form with a server in the middle
The browser should not talk directly to Telegram with your bot token. Instead, it sends ordinary form data to an endpoint you control. That endpoint validates the submission and makes the authenticated Telegram request.
/contact/ServerknowsBOT_TOKEN + CHAT_IDWe're using Django because How2Code runs on Django, but the same boundary works with Flask, FastAPI, Node, PHP, Laravel or a serverless function.
WHAT YOU NEED
Before you start
- A Telegram account
- A website with a backend you control
- Python and Django for this implementation
- A place to store environment variables in production
STEP 1
Create a Telegram bot
Open @BotFather in Telegram, start a chat and run /newbot. Choose a display name and username. BotFather will return an authentication token.
A bot token authenticates API requests as your bot. Do not paste it into HTML, browser JavaScript, screenshots or a public Git repository.
STEP 2
Get the destination chat ID
Send a message to your new bot first. Then call Telegram's getUpdates method from a trusted environment and inspect the returned message object's chat.id.
https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdatesFor a group, add the bot to the group and send a message there. Group and supergroup IDs can be negative. Keep the chat ID server-side too: it isn't an authentication secret like the token, but clients do not need it.
STEP 3
Create the website form
Start with normal HTML. This keeps the form usable and understandable before we add any JavaScript enhancement.
<form method="post" action="/contact/">
{% csrf_token %}
<label>Name
<input name="name" maxlength="80" required>
</label>
<label>Email
<input type="email" name="email" required>
</label>
<label>Message
<textarea name="message" maxlength="1000" required></textarea>
</label>
<button type="submit">Send</button>
</form>STEP 4
Create the secure backend endpoint
Django validates the fields before Telegram sees anything. The hidden honeypot is deliberately boring: many automated form bots fill every input they find.
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=80, strip=True)
email = forms.EmailField(max_length=254)
message = forms.CharField(max_length=1000, strip=True)
website = forms.CharField(required=False)
def clean_website(self):
if self.cleaned_data.get("website"):
raise forms.ValidationError("Invalid submission.")
return ""from django.shortcuts import render
from django.views.decorators.http import require_POST
@require_POST
def contact(request):
form = ContactForm(request.POST)
if not form.is_valid():
return render(request, "contact.html", {"form": form}, status=400)
send_to_telegram(
name=form.cleaned_data["name"],
email=form.cleaned_data["email"],
message=form.cleaned_data["message"],
)
return render(request, "contact_success.html")STEP 5
Send the message through Telegram Bot API
The authenticated request happens here, on the server. A short timeout matters: if Telegram is temporarily unreachable, your web worker should not hang indefinitely.
import json
import os
from urllib import request
def send_to_telegram(*, name, email, message):
token = os.environ["TELEGRAM_BOT_TOKEN"]
chat_id = os.environ["TELEGRAM_CHAT_ID"]
text = (
"New website lead\n\n"
f"Name: {name}\n"
f"Email: {email}\n"
f"Message: {message}"
)
payload = json.dumps({
"chat_id": chat_id,
"text": text,
}).encode("utf-8")
req = request.Request(
f"https://api.telegram.org/bot{token}/sendMessage",
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
with request.urlopen(req, timeout=5):
passFor one sendMessage call, Python's standard library keeps the example small and exposes the actual HTTP boundary. A bot framework makes sense when your application starts receiving commands, callbacks and updates.
STEP 6
Store secrets outside the code
Set the token and destination as environment variables on the machine or hosting platform running Django.
TELEGRAM_BOT_TOKEN=123456:replace-with-real-token
TELEGRAM_CHAT_ID=123456789Add local environment files to .gitignore. Never commit the real token. If a token has been exposed, revoke/regenerate it with BotFather rather than merely deleting it from the latest commit.
STEP 7
Make the public endpoint boring to abuse
A production form needs more than a hidden token. Our live demo uses server-side validation, Django CSRF protection, a honeypot, strict length limits, a fixed Telegram destination and request throttling.
Reject forged browser submissions.
Accept only the fields and sizes you expect.
Drop simple automated form fillers.
Cap repeated submissions per source.
Never accept a chat ID from the visitor.
Don't leak Telegram responses or secrets.
STEP 8
Test the failure cases, not only the happy path
- A valid submission reaches Telegram
- Empty name/message is rejected
- An invalid email is rejected
- The bot token is absent from page source and JavaScript
- Repeated submissions are throttled
- A Telegram outage produces a safe user-facing error
STEP 9
Deploy it
The deployment rule is simple: configure the two environment variables where your Django process starts, restart the application, then submit the production form once and verify the message arrives.
[Service]
Environment="TELEGRAM_BOT_TOKEN=..."
Environment="TELEGRAM_CHAT_ID=..."
ExecStart=/path/to/venv/bin/gunicorn config.wsgi:applicationUse your host's secret/environment mechanism when available. The exact configuration differs between systemd, Docker, Render, Railway, Fly.io and other platforms.
BUILD WITH AI · UNDERSTAND WHAT YOU SHIP
What we asked AI — and what still needed engineering
Build a Django contact form with name, email and message fields. When submitted, send the data to a Telegram bot. Keep the implementation simple.
A first AI answer can be working and still be unfinished
AI gets you from zero → working.
Engineering gets you from working → shippable.
FULL WORKING PATTERN
Keep the pieces small
The production pattern is only four responsibilities: HTML collects the data, a Django form validates it, a view controls the request, and a tiny Telegram client owns the external API call. That separation makes each part easy to replace later.
templates/contact.htmlform UIforms.pyvalidationviews.pyrequest + responsetelegram.pyTelegram APITROUBLESHOOTING
When the form doesn't reach Telegram
The API returns 401 Unauthorized
The bot token is invalid or has been revoked. Get the current token from BotFather and update the server environment.
The API returns “chat not found”
Check the chat ID and make sure the bot has access to that chat. For a private chat, message the bot first. For a group, add the bot and generate an update from that group.
getUpdates returns nothing
Send the bot a new message and try again. If the bot is configured to receive updates through a webhook, getUpdates cannot be used at the same time.
The form works locally but fails in production
Check that the production process actually receives the environment variables, can make outbound HTTPS requests, and was restarted after configuration changed.
Django returns CSRF verification failed
Render the CSRF token inside the form and submit to the same Django site. Don't disable CSRF just to make the error disappear.
POSSIBLE IMPROVEMENTS
Where to take it next
Once the basic path is reliable, you can store leads in your database, send an email fallback, attach page/UTM data, push the same lead into a CRM, or route different forms to different internal chats. Add those because your workflow needs them — not because the first version needs more code.
FAQ
Common questions
Can I send an HTML form directly to Telegram?
Technically a browser can call the Bot API, but doing so exposes the bot token to every visitor. Use a backend or serverless endpoint between the form and Telegram.
Do I need a Telegram webhook for a contact form?
No. A webhook is for Telegram sending updates to your application. Here your application is making an outbound sendMessage request to Telegram.
Can I use this without Django?
Yes. The important architecture is browser → your trusted server endpoint → Telegram Bot API. The middle layer can be implemented in Node, PHP, Flask, FastAPI, Laravel or a serverless function.
Is the Telegram chat ID secret?
The bot token is the authentication secret. The chat ID is not equivalent to a password, but there is no reason to expose it to the browser in this architecture.
HAVE A REAL PROJECT?
Build it yourself.
Or connect your real workflow.
Need this connected to your existing website, CRM, email, Telegram or WhatsApp? How2Code can build the production version around the tools you already use.
Tell us what you need →