NetSendo
Platform Updates

NetSendo v2.8.6: Locking Down Telegram Webhooks for Brain

NetSendo Team7 min read

When you connect your marketing AI to a chat app, the chat becomes a way to control your account. With NetSendo Brain linked to Telegram, you can switch the agent to autonomous mode, update its knowledge base and approve or reject campaign plans from your phone. That is handy. It also means whoever can talk to the webhook can do the same things.

NetSendo v2.8.6 is a focused security release that hardens the Telegram integration. It closes three related issues, and all of them matter most on shared, multi-account instances. If you use Brain with Telegram, upgrade now.

TL;DR: NetSendo v2.8.6 secures POST /api/telegram/webhook with a Telegram secret token, an HMAC of the bot token keyed with your APP_KEY. Updates without the token get a 403. Approval buttons now only act on pending plans from their own account and can't queue a plan twice. The shared bot's webhook is always pinned to APP_URL. Existing webhooks are secured automatically within five minutes by the scheduler.

🔐 What's New in v2.8.6

🛡️

Authenticated Webhook

Only Telegram, carrying the right secret token, can deliver updates.

✅

Account-Scoped Approvals

Approve/Reject buttons only act on your own pending plans.

📌

Pinned Shared Bot

The instance-wide bot always points to your own APP_URL.

1. The Telegram webhook only accepts updates from Telegram

Before this release, POST /api/telegram/webhook was a public endpoint that treated any JSON body as a real Telegram update. If someone knew the chat ID of a linked account, they could pretend to be that chat. They could send commands such as /mode autonomous, /knowledge or /disconnect, chat with Brain and press its approval buttons.

Think of it like a mail slot that accepted any letter with your name written on it. Now every letter also needs a wax seal that only Telegram can apply.

Here's how it works now. When NetSendo calls setWebhook, it gives Telegram a secret token, which is an HMAC of the bot token keyed with your APP_KEY. Telegram then sends that token with every update in the X-Telegram-Bot-Api-Secret-Token header. If the header is missing or wrong, the webhook returns 403.

POST /api/telegram/webhook HTTP/1.1
Host: netsendo.example.com
Content-Type: application/json
X-Telegram-Bot-Api-Secret-Token: <HMAC(bot_token, APP_KEY)>

{ "update_id": 123456, "message": { "chat": { "id": 987654 }, "text": "/mode autonomous" } }

Tokens are also tied to bots, not just to the instance:

  • A linked chat is only accepted through the bot of its own account. If you own one bot, you can't use its valid token to send updates as another account's chat.
  • A chat that isn't linked yet (/start, /connect) can arrive through any bot configured on the instance, so onboarding keeps working.
ℹ️ Credit: This issue was responsibly reported by realtasty. Thank you!

2. Approval buttons only decide plans from their own account

The Approve and Reject buttons used to look up an approval by its number alone. That meant a chat could approve or cancel a plan that belonged to a different account.

Now a button only acts on a plan that belongs to the account linked to that chat and is still waiting for a decision. Anything else gets the reply "This plan is no longer waiting for your approval". As a bonus, pressing Approve twice no longer queues the plan twice, so you won't get duplicate sends from a double tap.

3. One account can no longer redirect the shared bot

Instances can set an instance-wide bot through TELEGRAM_BOT_TOKEN. Before, Setup Webhook sent Telegram whatever URL the request gave it. Any account using the shared bot could point its webhook at its own server and receive every other account's updates.

The shared bot's webhook is now always set to APP_URL/api/telegram/webhook. If an account brings its own bot, its owner can still point it wherever they want.

💼 Why It Matters: Real-World Scenarios

🏢

Agencies Running Multi-Tenant Instances

If one NetSendo instance serves many clients on a shared bot, v2.8.6 stops one client from:

  • Capturing other clients' Brain conversations by redirecting the bot
  • Approving or cancelling another client's campaign plans
  • Sending commands into another tenant's chat
🛒

E-commerce Teams Approving Campaigns on Mobile

Store owners often approve Brain's abandoned-cart or post-purchase plans from Telegram. With signed updates and account-scoped buttons, nobody else can sneak in an approval or a cancellation. Duplicate taps no longer mean duplicate campaigns.

🚀

SaaS Teams Using Autonomous Mode

Autonomous mode lets Brain run onboarding and churn-prevention sequences with little supervision. Only the real, linked chat can now switch it on, so outsiders can't flip that mode remotely.

🛠️ Step-by-Step Upgrade Guide

  1. Back up and update

    Back up your database first, then pull the new release. With Docker:

    docker compose pull
    docker compose up -d
  2. Check that the scheduler is running

    The scheduled task brain:secure-telegram-webhooks adds the secret token to every bot whose webhook points at this instance, within five minutes. Telegram holds any updates it sends in the meantime and retries them, so you won't lose messages.

  3. Run the command by hand if needed

    If your scheduler isn't running, go to Brain → Settings → Telegram → Setup Webhook, or run the command below. Your container name may differ.

    docker compose exec app php artisan brain:secure-telegram-webhooks
  4. Re-set webhooks on other hosts

    The command doesn't touch webhooks that point to a host other than your APP_URL host. Set those again from the panel so they get the secret token.

  5. Verify

    Check the webhook status with the Telegram API:

    curl https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getWebhookInfo

    Make sure url ends in /api/telegram/webhook on your domain and that last_error_message doesn't report 403 errors. Then send /start to your bot to confirm it replies.

⚠️ Common pitfall: If your bot suddenly stops replying after the upgrade, the webhook probably hasn't been re-registered with the secret yet. Telegram's deliveries are being rejected with 403. Click Setup Webhook or run the artisan command and the queued updates will come through.
⚠️ Important: The secret is derived from APP_KEY. If you rotate APP_KEY, set up your webhooks again, or Telegram will keep sending the old token and get a 403.

🎯 Expert Tips

1
Keep the scheduler healthy

Make sure php artisan schedule:run runs every minute through cron or a scheduler container. Security fixes like this one rely on it.

2
Treat APP_KEY as a crown jewel

Store it in a secrets manager or a protected .env file. Never commit it, and limit who can read it on the server.

3
Give high-value accounts their own bot

For agencies, a dedicated bot per client isolates traffic further and lets each client control its own webhook destination.

4
Watch for 403 spikes

Log or alert on 403 responses from /api/telegram/webhook. A sudden burst may mean someone is probing your instance.

5
Run the test suite in CI

The fix is covered by tests/Feature/TelegramWebhookSecurityTest.php. If you maintain a fork, keep this test in your pipeline so the protection doesn't regress.

🌍 Also in This Release

  • ✅ New messages (such as the approval-state reply) are translated into English, Polish, German and Spanish
  • ✅ Feature tests cover the webhook security behavior
  • ✅ No database changes or manual config edits are required for most installs

🏁 Conclusion

Self-hosting means you own your data, and it also means you own your attack surface. NetSendo v2.8.6 makes sure that only Telegram can talk to your Brain webhook and that each chat can only control its own account. It also stops the shared bot from being redirected. For most installs, upgrading takes a single docker compose pull.

See the full details in the v2.8.6 release notes, read the documentation, or browse more integrations.

Secure Your Instance Today

Upgrade to NetSendo v2.8.6 and keep your Telegram-powered automation safe.

#security#telegram#webhooks#netsendo-brain#self-hosted#release
Share