Building a Multi-Channel AI Gateway: Facebook Messenger, Telegram, Zorgax and the MyZubster Metaverse
Building an AI chatbot is relatively easy.
Building an AI assistant that can live across multiple communication platforms, preserve application truth, route users into different services, deliver media, survive serverless execution, and remain maintainable is a different problem.
Over the last development cycle, we built a production bridge between Facebook Messenger, MyZubster, and Zorgax.
Now we're using what we learned from that integration to design the next channel: Telegram.
This article explains the architecture, the mistakes we encountered, and how we're moving toward a shared multi-channel gateway instead of creating a different AI bot for every platform.
The architecture we already have
Our Facebook Messenger integration currently follows this path:
Facebook User
↓
MyZubster Community Page
↓
Meta Messenger
↓
Meta Webhook
↓
MyZubster API
↓
Validation / Normalization / Deduplication
↓
Zorgax or Deterministic Router
↓
Marketplace
Seller
Metaverse
LIFE Pilot
Community
Comic Universe
↓
Meta Send API
↓
Messenger
The important part is the middle.
Messenger isn't connected directly to an unrestricted LLM.
There is an application layer between the messaging platform and Zorgax.
That distinction became increasingly important as the integration grew.
- Start with the webhook — but don't stop there
Meta sends Messenger events to our webhook:
POST /api/meta/messenger/webhook
Before processing anything, the bridge verifies that the request is legitimate.
The production implementation includes:
Webhook verification
X-Hub-Signature-256 validation
HMAC-SHA256
timing-safe comparisons
echo protection
event normalization
deduplication
But we discovered an important serverless lesson.
Returning:
200 OK
doesn't mean the AI workflow completed.
Meta wants the webhook acknowledged quickly, while the actual workflow may still need to:
receive message
↓
normalize it
↓
retrieve conversation context
↓
call Zorgax
↓
route application intent
↓
call Meta Send API
On Vercel, we therefore use waitUntil() to keep the asynchronous processing alive after acknowledging the webhook.
Conceptually:
app.post("/api/meta/messenger/webhook", (req, res) => {
const work = processMessengerEvent(req.body);
waitUntil(work);
res.sendStatus(200);
});
The actual implementation contains additional validation and error handling, but the architectural lesson is simple:
Webhook acknowledgement and workflow completion are two separate states.
- Normalize the channel before calling the AI
Messenger doesn't send only plain text.
A user can interact through:
text
quick replies
postbacks
attachments
Instead of making Zorgax understand raw Meta payloads, the bridge normalizes those events first.
Conceptually:
Meta payload
↓
Channel Adapter
↓
Normalized message
↓
Zorgax / Router
This gives us a much cleaner boundary.
The same idea becomes especially important when adding Telegram.
- Don't let the LLM own application truth
This was probably the most important lesson from production testing.
During one conversation, a user asked about becoming a Seller.
A follow-up question was:
How much does it cost?
Zorgax correctly understood that the user was still talking about Seller onboarding.
But the model generated unsupported assumptions about pricing and transaction fees.
The application already knew the configured Seller information.
The LLM shouldn't have been guessing.
We changed the architecture so commercial information is grounded in MyZubster state.
This eventually became an explicit architectural decision:
ADR-001 — Application Truth Before Generative Output
The principle is:
When MyZubster already knows a fact, application state takes precedence over unconstrained model generation.
This applies to things like:
commercial configuration
canonical URLs
public resource availability
Comic Universe assets
Metaverse destinations
verified application features
Zorgax can explain those facts.
It shouldn't invent them.
- Deterministic routing is sometimes better than AI
We discovered the same issue while connecting the MyZubster Comic Universe.
A user asked:
Show me the MyZubster comic
Initially, the request could still fall through to the generic AI path.
During testing, Zorgax incorrectly answered that the comic wasn't publicly available.
But MyZubster already knew:
the Comic Universe exists
the public route exists
public visual assets exist
There was nothing to reason about.
So we introduced deterministic routing.
User message
↓
Comic intent?
↓ yes
Select approved visual
↓
Send image
↓
Send grounded Comic response
↓
Comic Universe URL
The model no longer gets an opportunity to contradict known application state.
- Messenger became a gateway into the ecosystem
Once this architecture was working, Messenger stopped being just a chatbot interface.
It became a conversational gateway.
A user can ask Zorgax about:
Marketplace
Seller
Metaverse
LIFE Pilot
Community
Comic Universe
without knowing the site's navigation beforehand.
For example:
User:
Tell me about the Metaverse.
↓
Messenger
↓
Zorgax
↓
MyZubster routing
↓
https://www.myzubster.com/metaverse
That leads to a useful architectural distinction:
Facebook is the channel.
Messenger is the transport.
Zorgax is the conversational guide.
MyZubster is the ecosystem and application truth layer.
- The next mistake would be building everything again for Telegram
After getting Messenger working, the obvious approach would be:
metaMessengerRoutes.js
telegramBotRoutes.js
Messenger prompts
Telegram prompts
Messenger routing
Telegram routing
Messenger commercial logic
Telegram commercial logic
That would work initially.
It would also become technical debt quickly.
Imagine changing Seller pricing rules.
Now two integrations need updating.
Add Discord?
Three.
Add another channel?
Four.
Instead, we're designing Telegram around a shared channel architecture.
- The target multi-channel architecture
The direction is:
MyZubster
│
▼
Zorgax
│
▼
Shared Channel Core
/ | \
/ | \
▼ ▼ ▼
Messenger Telegram Future
A more detailed version:
MyZubster Application
│
▼
Application Truth Layer
│
▼
Zorgax
│
┌─────────────┴─────────────┐
│ │
Deterministic Router Conversational AI
│ │
└─────────────┬─────────────┘
│
Channel Core
/ \
/ \
▼ ▼
Meta Adapter Telegram Adapter
│ │
▼ ▼
Messenger Telegram Bot
The transport changes.
The application intelligence doesn't need to.
- What belongs in a channel adapter?
The Telegram adapter should understand Telegram.
The Messenger adapter should understand Meta.
For example, the Telegram layer should own:
Telegram webhook validation
Update parsing
chat_id
user_id
callback queries
Telegram commands
sendMessage
sendPhoto
inline keyboards
Telegram API errors
Messenger owns things such as:
Meta webhook signatures
Page sender IDs
quick replies
postbacks
Meta Send API
Messenger attachments
is_echo
Neither should independently decide what the current Seller plan means.
That belongs higher in the architecture.
- Telegram webhook design
The proposed Telegram endpoint is:
POST /api/telegram/zorgax/webhook
with a status endpoint:
GET /api/telegram/zorgax/status
The processing pipeline would be:
Telegram Update
↓
Validate webhook secret
↓
Check update_id
↓
Deduplicate
↓
Normalize
↓
Shared router
↓
Known intent?
↙ ↘
yes no
↓ ↓
deterministic Zorgax
route
↘ ↙
Telegram response
- Telegram security
We don't want to treat possession of the bot endpoint as authorization.
The planned configuration separates credentials:
TELEGRAM_BOT_TOKEN
TELEGRAM_WEBHOOK_SECRET
TELEGRAM_BOT_USERNAME
The webhook secret authenticates inbound webhook traffic.
The Bot Token authorizes calls to Telegram's Bot API.
Those are different responsibilities.
Secrets should never appear in:
Git commits
documentation
screenshots
PR descriptions
runtime logs
status endpoints
- Deduplicate Telegram updates
Telegram provides an update_id.
That gives us a natural idempotency key.
Conceptually:
if (recentUpdates.has(update.update_id)) {
return;
}
recentUpdates.add(update.update_id);
await processUpdate(update);
A bounded in-memory cache is enough for an initial implementation.
At larger scale, this should become a distributed idempotency mechanism.
The important rule is:
Never design a webhook consumer assuming delivery happens exactly once.
- Private chats and groups are different products
One important Telegram design decision is not to make Zorgax automatically answer everything inside a group.
In private chat:
User → Zorgax
Automatic conversational replies make sense.
In a Telegram group, responding to every message could quickly become annoying.
Our proposed initial group policy is:
/command
@bot mention
reply to Zorgax
explicit button interaction
Only those interactions invoke the assistant.
That makes the bot participate in the community rather than dominate it.
- Commands should be shortcuts, not separate applications
The first command surface could be:
/start
/help
/marketplace
/seller
/metaverse
/life
/comic
/culture
/privacy
But /metaverse shouldn't contain a separate implementation of Metaverse logic.
It should simply produce an intent for the shared router:
/metaverse
↓
Telegram adapter
↓
intent: metaverse
↓
shared MyZubster router
Natural language:
Where is the MyZubster Metaverse?
should reach the same destination.
- Media should also be shared conceptually
Messenger can already deliver Comic Universe imagery.
Telegram can eventually implement the same application behavior using sendPhoto.
Comic request
↓
Shared Comic routing
↓
Approved public asset
↓
Channel adapter
/ \
/ \
Meta Telegram
│ │
image sendPhoto
The application decides what should be sent.
The adapter decides how that platform sends it.
- Keep fiction and evidence separate
MyZubster contains narrative visual material.
We explicitly distinguish:
FICTION / CONCEPT
from evidence of real-world events.
That semantic rule belongs in the shared application layer.
It should therefore apply equally to:
Website
Messenger
Telegram
future channels
Changing communication platforms should never change the meaning of the underlying content.
- From messaging integration to community continuity
There's another reason Telegram is interesting for this architecture.
We're exploring how MyZubster can preserve connections formed around physical events.
Think about a gathering involving:
music
art
people
local initiatives
recycling
community activity
Normally the event finishes and much of that temporary network disappears.
A multi-channel MyZubster gateway creates another possibility:
Physical Event
↓
People meet
↓
Local / recycling activity
↓
QR code
↙ ↘
Messenger Telegram
↘ ↙
Zorgax
↓
MyZubster
↓
┌─────┼─────────┐
▼ ▼ ▼
LIFE Culture Metaverse
↓
Community
↓
Next activity
The digital layer doesn't replace the event.
Its purpose is to preserve and extend connections created in the physical world.
- Culture and Subculture
This also opens an interesting direction for MyZubster Culture/Subculture spaces.
A community around:
electronic music
sound systems
digital art
environmental action
local culture
open technology
creative communities
could use Messenger or Telegram as its lightweight communication entry point.
Zorgax can then guide people toward the corresponding MyZubster space.
But there's an important grounding rule here too.
The assistant must never invent:
a Culture that doesn't exist
an event that hasn't been registered
a partnership that hasn't been verified
an official affiliation that hasn't been established
Again:
application truth before generation.
- Recycling and LIFE Pilot
The same model can connect event participation with environmental initiatives.
For example:
Event
↓
QR
↓
Telegram
↓
Zorgax
↓
"How can I help with cleanup?"
↓
LIFE Pilot
Eventually this could help communities distribute cleanup instructions, organize volunteers, preserve public resources and document real initiatives.
But environmental claims need the same grounding discipline.
If MyZubster doesn't have evidence that something was collected, recycled or measured, Zorgax shouldn't claim that it happened.
- Identity requires special care
A Telegram identity is not automatically a MyZubster identity.
Neither is a Facebook identity.
If we eventually connect accounts, the flow should require explicit authorization.
Something like:
Telegram user
↓
/link
↓
Temporary MyZubster authorization URL
↓
MyZubster login
↓
Explicit consent
↓
Server-side account link
Never:
Telegram username == MyZubster username
Usernames aren't identity proof.
- Cross-channel memory is a later problem
Messenger currently uses lightweight short-term conversation context.
Telegram can initially do the same.
But we shouldn't pretend that:
Facebook user A
and:
Telegram user B
are the same person simply because their names look similar.
Cross-channel continuity should only happen after explicit account linking.
Until then:
Messenger context ≠ Telegram context
That's safer and architecturally cleaner.
- Proposed implementation phases
We're planning the Telegram work incrementally.
Phase 1 — Private chat MVP
Telegram Bot
Webhook
Secret validation
Text messages
Commands
Zorgax
Seller grounding
Metaverse routing
Deduplication
Logging
Phase 2 — Media and navigation
Comic sendPhoto
Inline keyboards
Culture routing
LIFE routing
Better fallbacks
Phase 3 — Community mode
Groups
Mention-only interaction
Command mode
Admin-controlled behavior
Context isolation
Event deep links
Phase 4 — Cross-channel architecture
Messenger ─┐
Telegram ──┼→ Shared session/application layer
Future ────┘
Only after explicit identity and privacy rules are defined.
- What “done” means
We don't consider the Telegram integration complete simply when /start replies.
The first production milestone should require:
✓ real Telegram user can message the bot
✓ webhook authentication works
✓ duplicate updates don't duplicate replies
✓ Zorgax follows the user's language
✓ short-term Seller context works
✓ commercial information is grounded
✓ Metaverse route works
✓ Comic media works or safely falls back
✓ unsupported media gets an honest response
✓ secrets don't appear in logs
✓ group behavior is controlled
✓ operational documentation matches production
That's a much more useful definition of “working”.
The documentation is open source
We documented the existing production Messenger architecture here:
Meta Messenger ↔ Zorgax Bridge Guide
The production operations runbook is here:
Meta Messenger Operations Runbook
And the new Telegram architecture and implementation plan is here:
Telegram ↔ Zorgax Bridge Plan
The complete project:
MyZubster on GitHub
You can also explore the connected application surfaces:
MyZubster
MyZubster Metaverse
MyZubster Marketplace
MyZubster Comic Universe
Final takeaway
The architecture we're moving toward isn't:
Facebook AI bot
Telegram AI bot
another AI bot
another set of prompts
another copy of business logic
It's:
PEOPLE
│
┌──────────┼──────────┐
▼ ▼ ▼
Messenger Telegram Future
└──────────┼──────────┘
▼
CHANNEL ADAPTERS
│
▼
ZORGAX CORE
│
▼
MYZUBSTER
┌─────────┼─────────┐
▼ ▼ ▼
Marketplace Metaverse LIFE
│ │ │
└──── Culture ──────┘
│
▼
COMMUNITY
The messaging platform should be replaceable.
The application's truth should not be.
And the AI should be the layer that helps people navigate the system — not the layer that invents the system.
That's the direction we're taking with Zorgax and MyZubster.
Top comments (0)