When a Telegram bot runs in production, long polling—constantly asking Telegram "any new messages?"—works, but it is not ideal. Webhooks flip the model: Telegram pushes each update to your HTTPS endpoint as soon as it happens. That means lower latency, cleaner scaling behind a load balancer, and a bot that lives naturally inside an ASP.NET Core app alongside your REST APIs.
This guide shows how to integrate Telegram webhooks in .NET: when to choose them, what Telegram requires, a complete ASP.NET Core example, local development with a tunnel, and practical use cases.
Prerequisite: If you have not created a bot yet, start with How to Build a Telegram Bot with C# for BotFather setup and Telegram.Bot basics.
What is a Telegram webhook?
With a webhook, you register a public HTTPS URL with Telegram. When a user sends a message, taps a button, or triggers any subscribed event, Telegram sends an HTTP POST to that URL with a JSON Update payload. Your server processes the update and calls the Bot API (for example sendMessage) to respond.
Your handler should return 200 OK quickly. Telegram may retry if the response is slow or missing—design for idempotency when side effects matter.
Webhook vs long polling
| Aspect | Long polling | Webhook |
|---|---|---|
| Connection model | Bot pulls updates (getUpdates) |
Telegram pushes updates to your URL |
| Hosting | Any machine with outbound HTTPS | Public HTTPS URL required |
| Latency | Small poll interval delay | Near-instant delivery |
| Scale | One active poller per bot token | Stateless handlers behind a load balancer |
| Local dev | Simple (dotnet run) |
Needs a tunnel (ngrok, Cloudflare Tunnel) |
| Best for | Prototyping, local testing | Production, alerts, multi-instance deploys |
The long-polling guide is the fastest way to get a bot running on your laptop. For 24/7 cloud deployment, webhooks are the production default.
When to use webhooks
Use webhooks when:
- The bot runs 24/7 on Azure App Service, AWS, a VPS, or Kubernetes
- You need low-latency delivery—alerts, support replies, transactional notifications
- You want horizontal scaling: multiple app instances share one webhook URL behind a load balancer
- The bot is part of a larger ASP.NET service—same host as REST APIs, background jobs, or admin dashboards
Stick with long polling when you are prototyping locally and do not want to expose a public URL yet.
Requirements
Before calling setWebhook, make sure you have:
- Valid HTTPS URL — Telegram requires HTTPS with a trusted certificate. Self-signed certs are rejected in normal setups.
- Bot token — from @BotFather. Load from environment variables, Azure Key Vault, or .NET user secrets—never commit tokens to source control.
- Secret token (recommended) — a random string you pass to
setWebhook. Telegram sends it back in theX-Telegram-Bot-Api-Secret-Tokenheader so you can reject forged requests. - Reverse proxy awareness — if you terminate TLS at nginx, Azure Front Door, or Cloudflare, enable ASP.NET Core Forwarded Headers middleware so your app sees the correct scheme and client IP.
Complete example: ASP.NET Core webhook bot
This echo bot mirrors the long-polling tutorial but uses webhooks and ASP.NET Core minimal APIs.
Create the project
dotnet new web -n TelegramWebhookBot
cd TelegramWebhookBot
dotnet add package Telegram.Bot
Pin a local port in Properties/launchSettings.json:
"applicationUrl": "http://localhost:5140"
Configuration
Add to appsettings.Development.json:
{
"Telegram": {
"BotToken": "YOUR_BOT_TOKEN_HERE"
}
}
Minimal API implementation
Update Program.cs:
using System.Text.Json;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ITelegramBotClient>(_ =>
{
var token = builder.Configuration["Telegram:BotToken"]
?? throw new InvalidOperationException("Bot token missing");
return new TelegramBotClient(token);
});
var app = builder.Build();
app.MapGet("/", () => "Telegram bot is running");
app.MapPost("/telegram/webhook", async (
Update update,
ITelegramBotClient botClient,
ILogger<Program> logger) =>
{
try
{
if (update.Type == UpdateType.Message &&
update.Message?.Text is not null)
{
var chatId = update.Message.Chat.Id;
var text = update.Message.Text;
logger.LogInformation("Message received: {Text}", text);
await botClient.SendMessage(
chatId: chatId,
text: $"Echo: {text}");
}
return Results.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "Webhook processing failed");
return Results.Problem();
}
});
app.Run();
How it works
ITelegramBotClient— singleton client for Bot API calls (SendMessage,SetWebhook, etc.).MapPost("/telegram/webhook")— ASP.NET Core deserializes the JSON body into anUpdateautomatically.
Run API in cloud
Telegram cannot reach http://localhost:5140 directly. Create Azure Web App and deploy your app there:
dotnet build
dotnet publish -c Release -o publish
cd publish
zip -r app.zip .
az webapp deploy \
--resource-group rg \
--name telegram-webhook \
--src-path app.zip
Register webhook in Telegram
Open browser:
https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook?url=https://<YOUR_SERVER>/telegram/webhook
Telegram should return:
{
"ok": true,
"result": true
}
Webhook registration uses Telegram setWebhook.
Test
Send a message to your bot in Telegram.
Your API receives:
{
"update_id": 123456,
"message": {
"text": "hello"
}
}
And replies:
Echo: hello
Useful Telegram API endpoints
Set webhook:
https://api.telegram.org/bot<BOT_TOKEN>/setWebhook?url=<URL>
Get webhook info:
https://api.telegram.org/bot<BOT_TOKEN>/getWebhookInfo
Delete webhook:
https://api.telegram.org/bot<BOT_TOKEN>deleteWebhook
Production checklist
- Secrets — store
BotTokenin Key Vault or environment variables, notappsettings.jsonin the repo. - Return 200 quickly — offload heavy work (LLM calls, database writes, external APIs) to a background queue if processing takes more than a second or two.
- Structured logging — log
update.Idandchat.Id; avoid dumping full message bodies in production logs. - Idempotency — Telegram may retry failed deliveries; track processed update IDs if your handler has side effects.
- TLS at the edge — terminate HTTPS at your ingress; do not expose plain HTTP publicly.
Use cases
Webhooks fit any scenario where Telegram is a real-time notification or interaction channel for your .NET backend.
- DevOps and SRE alerts — CI pipeline failures, deployment status, and monitoring thresholds pushed to a team channel. Your ASP.NET app receives GitHub/Azure DevOps webhooks and forwards summaries to Telegram.
- Customer support bot — inbound user messages arrive via webhook; your handler creates tickets in Jira, Zendesk, or a custom CRM and replies with a case number.
- E-commerce notifications — order confirmed, shipped, and delivered updates triggered from your order service without a polling loop.
- Internal command bot —
/deploy,/status, or/rollbackcommands parsed in the webhook handler and routed to internal APIs on the same host. - Payment and fintech alerts — transaction confirmations and fraud flags delivered instantly; outbound API calls from the handler use proper auth and audit logging.
- IoT and edge alerts — device offline or threshold breach events from your telemetry pipeline forwarded to on-call engineers in Telegram.
- Lead capture — website form submission hits your API; the handler posts a formatted summary to a sales Telegram group.
- Multi-tenant SaaS — one ASP.NET service hosts multiple bots on different webhook paths (
/telegram/webhook/{tenantId}), each with its own token and secret.
Webhook-driven alerting is a common pattern in API monitoring products—push a failure notification the moment a health check fails instead of waiting for the next poll cycle.
Troubleshooting
| Symptom | Likely cause |
|---|---|
SetWebhook returns an error |
Invalid HTTPS certificate, URL not publicly reachable, or wrong port |
| No updates received | Webhook not registered, wrong WebhookUrl, or firewall blocking Telegram |
401 Unauthorized on webhook |
SecretToken mismatch between config and SetWebhook call |
| Duplicate replies | Both polling and webhook active, or multiple instances without idempotency |
| Slow or timing out | Handler does too much work synchronously—move to a background queue |
| Wrong scheme behind proxy | Missing UseForwardedHeaders() — Telegram sees HTTP instead of HTTPS |
Use GetWebhookInfo to inspect the registered URL, last error message, and pending update count:
var info = await bot.GetWebhookInfo();
Console.WriteLine($"URL: {info.Url}, LastError: {info.LastErrorMessage}");
Conclusion
Telegram webhooks turn your bot into a standard HTTP endpoint: Telegram pushes updates, your ASP.NET Core app handles them, and you scale like any other web service. Start with the echo example, tunnel locally for testing, then add secret token validation, background processing, and deployment behind HTTPS.
For the first bot and local prototyping, use How to Build a Telegram Bot with C# with long polling. When you deploy to the cloud, switch to webhooks with the pattern above.
Related reading
- How to Build a Telegram Bot with C# — BotFather setup and long polling
- Building MCP Server Using .NET — expose tools to an LLM; combine with a Telegram webhook to build an AI assistant bot
- Run MCP Server in Docker — container deployment patterns that apply to webhook bots too