
Building an AI Summarized Email Alert Workflow with Gmail, Gemini, and Telegram
Important emails, like the Q3 Client Dashboard update shown in the New Event alert, get missed or read late when they sit in an inbox alongside routine messages. Checking Gmail manually does not scale, and a plain forwarding rule does not help either, since it copies the raw email without surfacing what actually matters in it. The goal for this specific workflow was to catch a new email the moment it arrives, extract the actual substance of it, and push that summary to the channels that get attention immediately, while still keeping a permanent record of what happened.
The flow connects four nodes end to end. Gmail Trigger polls for new messages and passes each one into Gemini AI, which generates a summary of the content. From there the flow branches into three parallel actions, Telegram for an instant message alert, Logger Plugin to append the event to a permanent file record, and System Notifier to raise a local desktop alert. All three branches run concurrently off the same Gemini output rather than sequentially, so the Telegram alert does not wait on the file write to finish.
An automated email triage workflow that watches a Gmail inbox, summarizes incoming messages with Gemini AI, and fans the summary out to Telegram, a persistent log file, and a desktop notification, all built as a single flow on the visual canvas.
Table of Contents
Overview
This is a case study of a personal engineering project, not a client deliverable, and I am writing it that way on purpose. There are no invented user counts, no manufactured ROI percentages, and no fabricated testimonials in this post. What follows is a real account of the problem I set out to solve, the decisions I made while building it, where those decisions held up, and where they did not.
The project is a workflow automation platform similar in spirit to tools like Zapier or n8n. Users connect triggers, such as a new Gmail message, to actions, such as summarizing that message with Gemini and forwarding it to Slack or Telegram, using a visual canvas rather than writing code.
The Problem I Was Actually Solving
I was not trying to replace an existing product. I was trying to answer a specific engineering question for myself: what does it take to build a system where new integrations can be added without touching the core codebase at all.
Most small automation scripts I had written before this were monolithic. Every integration lived in the same file as the trigger logic and the execution logic. Adding a new service meant editing code that also handled unrelated services, which is exactly the kind of coupling that causes regressions. I wanted to test whether a microkernel architecture, where the core only handles plugin discovery, event routing, and flow state, could actually deliver on the promise of clean extensibility, or whether that promise falls apart once real complexity like credential handling and deduplication gets involved.
Constraints
A few constraints shaped every decision after this point:
Architectural Decisions and Why They Were Made
Microkernel core
The core system does three things only: discover and load plugins, route events through an event bus, and track flow execution state. Every integration, Gmail, Gemini, Slack, Telegram, WhatsApp, file logging, and console logging, lives as an independent plugin outside the core.
The test of whether this actually worked came later, when I added the WhatsApp and Telegram plugins after the Gmail and Slack plugins were already built and working. Neither addition required a change to plugin_manager.py, flow_engine.py, or either event bus. That is the concrete evidence that the architecture did what it was supposed to do, rather than a claim I am making without something to point to.
Two event buses, not one
I built both a synchronous event bus and an asynchronous one, with separate CLI entry points to run each. This was not indecision. I wanted to directly compare behavior under the same workflow, particularly once a plugin like Gemini introduces real network latency into the chain.
The honest result: the synchronous bus was easier to debug because event order was always predictable, but it visibly stalled once more than one plugin was making an external API call in the same flow. The asyncio based bus solved that stall, at the cost of being harder to trace when something did go wrong, since concurrent event handling means log output interleaves across plugins. Neither version is strictly better. They are a tradeoff between predictability and throughput, and keeping both in the codebase was useful specifically because it made that tradeoff visible instead of theoretical.
Per plugin credential isolation
Each plugin declares which of its configuration fields are secrets in its metadata file. When a user submits credentials through the web UI, the backend writes them to a .env file scoped to that plugin's own folder, and a root level .gitignore rule keeps every .env file out of version control automatically.
This design was chosen over a single centralized secrets file specifically because it limits blast radius. If a plugin is removed or found to be misbehaving, its credentials are contained to its own folder and do not require untangling from a shared configuration file.
Deduplication for polling triggers
The Gmail plugin polls for new messages, which introduces an obvious risk of reprocessing the same email multiple times. The solution combines a workflow start timestamp filter with an in memory cache of processed message IDs during the active session. This is a small detail in the codebase, but it is the difference between a trigger that behaves correctly and one that spams downstream actions with duplicate events the moment it starts running.
Where the Design Falls Short
A case study is not honest if it only lists what worked. Here is what did not, or what remains unresolved:
I am including these limitations because a case study that only reports success is not a case study, it is marketing copy, and the two should not be confused.
What I Would Change With More Time
If I extended this project further, the priority order would be plugin sandboxing first, since it is the largest actual risk in the current design, followed by moving the deduplication cache to a persisted store so restarts do not reopen the duplicate window, and only after that would I look at horizontal scaling, since single node execution is a reasonable limitation for a project at this stage.
Takeaway
The core question I was testing, whether a microkernel plugin architecture can deliver real extensibility once credential handling and deduplication are involved, held up under the additions I made to the project. The event bus comparison gave a genuine, verifiable tradeoff rather than an assumed one. The unresolved limitations, particularly around sandboxing and persistence, are the honest boundary of what this version of the system can be trusted to do.


