---
title: "Webhook to Telegram: Transform & Forward Any Payload to a Chat | WebhookRelay"
meta:
  "og:description": "Send a webhook to Telegram. Step-by-step guide to forward an incoming webhook into a Telegram chat, transforming the raw payload into the Bot API's sendMessage format in flight."
  "og:title": "Webhook to Telegram: Transform & Forward Any Payload to a Chat"
  description: "Send a webhook to Telegram. Step-by-step guide to forward an incoming webhook into a Telegram chat, transforming the raw payload into the Bot API's sendMessage format in flight."
---

![Stripes](https://webhookrelay.com/blog/webhook-to-telegram/images/stripes.svg)

# **Webhook to Telegram: Transform & Forward Any Payload to a Chat**

Send a webhook to Telegram. Step-by-step guide to forward an incoming webhook into a Telegram chat, transforming the raw payload into the Bot API's sendMessage format in flight.

![Webhook to Telegram: Transform &amp; Forward Any Payload to a Chat](https://webhookrelay.com/blog/webhook-to-telegram/images/blog/heroes/route.jpg)

You have a service that fires webhooks — a CI pipeline, a contact form, an uptime monitor, a payment provider, an alerting tool — and you want each event to show up in a **Telegram chat**. The problem: Telegram doesn't accept arbitrary payloads. Its Bot API expects a specific JSON body, and the webhook your source sends almost never matches that shape.

[Webhook Relay](https://webhookrelay.com) sits in the middle. It receives the incoming webhook at a stable public URL, **transforms** the payload into the format the Telegram Bot API expects, and delivers it — no glue server, no Lambda, no maintenance.

## [How it works](#how-it-works)

The flow is a single hop with a transform in the middle:

```
Your service  ──▶  Webhook Relay  ──▶  transform to { chat_id, text }  ──▶  Telegram chat
 (raw webhook)      (public URL)        (serverless function)              (message appears)
```

To post a message, you call the Telegram Bot API's `sendMessage` method by POSTing JSON to:

```
https://api.telegram.org/bot<TOKEN>/sendMessage
```

with a body like:

```
{ "chat_id": "123456789", "text": "your message" }
```

A generic incoming webhook isn't shaped like that, so we add a small [transformation function](https://webhookrelay.com/blog/webhook-to-telegram/features/transform-webhooks/) that builds the `{ chat_id, text }` body from the raw payload before delivery. That one feature is what turns Webhook Relay into a universal "anything → Telegram" bridge.

## [Step 1: Create a bot and get its token](#step-1-create-a-bot-and-get-its-token)

Open Telegram and start a chat with **@BotFather**. Send `/newbot`, give the bot a name and a username, and BotFather replies with an **HTTP API token** that looks like:

```
123456789:AAExampleTokenStringFromBotFather
```

Keep this token secret — anyone who has it can send messages as your bot.

## [Step 2: Find your chat_id](#step-2-find-your-chat_id)

Telegram won't let your bot message you until you've talked to it first, so send any message (like `/start`) to your new bot. Then fetch recent updates by opening this URL in a browser, replacing `<TOKEN>` with yours:

```
https://api.telegram.org/bot<TOKEN>/getUpdates
```

In the JSON response, find `result[].message.chat.id` — that number is your **chat_id**. For a group chat, add the bot to the group, post a message there, and read the (usually negative) group `chat.id` from the same `getUpdates` output.

## [Step 3: Create a Webhook Relay public endpoint](#step-3-create-a-webhook-relay-public-endpoint)

Open [the new public destination page](https://my.webhookrelay.com/new-public-destination) and paste the Bot API URL as the **destination**:

```
https://api.telegram.org/bot<TOKEN>/sendMessage
```

Webhook Relay gives you back an **input URL** — a public endpoint that you'll point your source service at. Everything sent to it gets forwarded toward Telegram.

## [Step 4: Add a transformation function](#step-4-add-a-transformation-function)

Right now, anything you send to the input URL would be forwarded to `sendMessage` _as-is_ — and Telegram would ignore it, because it isn't in `{ "chat_id": "...", "text": "..." }` shape. Add a [transformation](https://webhookrelay.com/blog/webhook-to-telegram/features/transform-webhooks/) to fix that.

Click **Transform** on the input, open the [Functions](https://my.webhookrelay.com/functions) page, create a function "from scratch", and paste this Lua:

```
local json = require("json")

-- the chat the message should land in
local chat_id = "123456789"

local payload = {
    chat_id = chat_id,
    text = r.RequestBody,
}

local body, err = json.encode(payload)
if err then error(err) end

r:SetRequestHeader("content-type", "application/json")
r:SetRequestMethod("POST")
r:SetRequestBody(body)
```

This takes the raw incoming body as the message `text`, pairs it with your `chat_id`, sets the right headers and method, and lets Webhook Relay deliver it. Telegram now accepts the call and posts the message to your chat.

## [Step 5: Point your source at the URL and test](#step-5-point-your-source-at-the-url-and-test)

Configure your source service (the CI tool, form, monitor, payment provider — whatever you're integrating) to send its webhook to the Webhook Relay **input URL**.

To confirm everything works before wiring up the real service, fire a quick test with `curl`:

```
curl -X POST https://my.webhookrelay.com/v1/webhooks/your-input-id \
  -d 'Deployment finished: build #482 passed on main'
```

Within a second or two, **Deployment finished: build #482 passed on main** appears in your Telegram chat. If nothing shows up, open the request log in your dashboard to see exactly what was received and delivered.

## [Going further](#going-further)

Once the basic bridge works, the transform function is where the real power is.

**Build a formatted message.** If your source sends JSON, parse it and construct a richer `text` string. Telegram supports Markdown and HTML formatting, so you can add bold headers and bullet points:

```
local json = require("json")

local event = json.decode(r.RequestBody)

local text = "*Deployment " .. event.status .. "*\n" .. event.message

local payload = {
    chat_id = "123456789",
    text = text,
    parse_mode = "Markdown",
}

local body, err = json.encode(payload)
if err then error(err) end

r:SetRequestHeader("content-type", "application/json")
r:SetRequestMethod("POST")
r:SetRequestBody(body)
```

**Filter out noise.** Not every event deserves a ping. Use [forwarding rules](https://webhookrelay.com/blog/webhook-to-telegram/features/forwarding-rules/) to only deliver events that match a condition, or drop them in the function with an early `return`.

**Fan out to multiple chats.** Add more destinations so one incoming webhook lands in several Telegram chats — or in Telegram _and_ Discord — at the same time. See [forwarding to multiple destinations](https://webhookrelay.com/blog/webhook-to-telegram/features/webhook-multiple-destinations/).

The pattern here is identical to the one in [Send a webhook to Discord](https://webhookrelay.com/blog/webhook-to-telegram/blog/webhook-to-discord/) — only the destination URL and the body shape change. For a complete real-world walkthrough, see [TradingView alerts](https://webhookrelay.com/blog/webhook-to-telegram/blog/trading-view/), where a plain alert message is transformed into a chat notification.

## [FAQ](#faq)

**What does Telegram need to receive?** A `POST` with a JSON body of `{ "chat_id": "...", "text": "your message" }` to `https://api.telegram.org/bot<TOKEN>/sendMessage`. The transform function produces exactly that.

**Do I need to run a server?** No. Webhook Relay receives, transforms, and delivers everything in the cloud. There's nothing to host or keep online.

**How do I see the raw payload my source sends?** Send it to a free [Webhook Bin](https://webhookrelay.com/blog/webhook-to-telegram/webhook-bin/) first to inspect the exact body and headers, then write your transform around that structure.

## [Get started](#get-started)

[Create a free Webhook Relay account](https://my.webhookrelay.com/register) and set up your first "anything → Telegram" bridge in a few minutes. Not sure what your source sends? [Inspect the payload in a Webhook Bin](https://webhookrelay.com/blog/webhook-to-telegram/webhook-bin/) first, then write the transform to match.