{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreic5ll6qr4w5eoycbnciaz3nytdv5dcrsc7wrxjb4sce3gfsoe6xby",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpmck6aetqm2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreihjrvesnuy4i4jwy5sjgw6w3i2kahk5aof4nsaqudv6o2ajgsbece"
    },
    "mimeType": "image/webp",
    "size": 38558
  },
  "path": "/tinytaskpro/how-i-built-a-gmail-automation-chrome-extension-using-javascript-and-flask-3af9",
  "publishedAt": "2026-07-01T19:21:41.000Z",
  "site": "https://dev.to",
  "tags": [
    "automation",
    "javascript",
    "python",
    "showdev",
    "automation software",
    "TinyTask Mailer Pro",
    "https://tinytask.org"
  ],
  "textContent": "Building browser automation software sounds simple until you start dealing with real-world limitations.\n\nOver the past few months, I've been developing TinyTask Mailer Pro, a Chrome extension that automates Gmail campaigns while keeping the user in control. It integrates Google Sheets, manages campaign progress, supports licensing, and communicates with a Flask backend for usage tracking.\n\nThis article isn't about sending spam or bypassing Gmail restrictions. Instead, it's about the engineering decisions, architecture, and lessons I learned while building a production-ready automation tool.\n\nWhy Build a Gmail Automation Extension?\n\nMany small businesses, freelancers, recruiters, and sales teams spend hours performing repetitive tasks like:\n\nOpening Gmail\nCopying recipient emails\nWriting personalized messages\nSending emails one by one\nTracking progress manually\n\nI wanted to build a tool that reduces repetitive work while still keeping the user involved in the process.\n\nThe goal was to save time—not replace responsible email practices.\n\nTech Stack\n\nHere's what powers the project:\n\nFrontend\nHTML\nCSS\nJavaScript\nChrome Extension APIs\nBackend\nPython\nFlask\nGunicorn\nNginx\nSQLite\nDeployment\nUbuntu VPS\nHTTPS API\nLicense server\n\nThis combination turned out to be lightweight, affordable, and easy to maintain.\n\nOverall Architecture\nGoogle Sheets\n│\n▼\nChrome Extension\n│\n▼\nGmail Web Interface\n│\n▼\nFlask API\n│\n▼\nSQLite Database\n\nThe extension handles browser automation while the backend remains the source of truth for:\n\nLicense validation\nMachine activation\nDaily quotas\nUsage statistics\nAccount management\n\nKeeping responsibilities separated made debugging significantly easier.\n\nReading Data from Google Sheets\n\nInstead of importing CSV files repeatedly, the extension reads campaign data directly from Google Sheets.\n\nTypical columns include:\n\nName\nEmail\nSubject\nMessage\n\nThis allows users to edit campaigns without rebuilding or re-uploading anything.\n\nWhenever changes are made in Google Sheets, the extension can work with the updated data.\n\nPersonalizing Every Email\n\nNobody likes receiving generic emails.\n\nEach message supports placeholders like:\n\nHello {Name},\n\nwhich become\n\nHello Sarah,\n\nduring the sending process.\n\nSimple personalization greatly improves the user experience while keeping implementation straightforward.\n\nManaging Long Campaigns\n\nOne challenge appeared very quickly.\n\nWhat happens if:\n\nChrome crashes?\nWindows restarts?\nThe browser closes?\nThe user clicks Stop?\n\nLosing campaign progress wasn't acceptable.\n\nInstead of storing temporary variables in memory, campaign progress is saved using chrome.storage.local.\n\nInformation such as:\n\nCurrent row\nEmails sent\nPending emails\nDelay settings\nCampaign status\n\ncan all survive browser restarts.\n\nThis became one of the most valuable improvements in the project.\n\nBuilding Resume Support\n\nResume functionality sounds simple.\n\nIt isn't.\n\nThe extension needs to restore:\n\nCurrent spreadsheet row\nCampaign status\nDelay timer\nStatistics\nCurrent Gmail account\n\nwithout accidentally sending duplicate emails.\n\nCarefully restoring state while preventing duplicates required much more testing than expected.\n\nRandom Delays Matter\n\nSending hundreds of emails instantly isn't realistic.\n\nInstead, every send operation waits for a randomized interval.\n\nFor example:\n\n7 seconds\n12 seconds\n9 seconds\n14 seconds\n\nrather than\n\n10\n10\n10\n10\n\nThe goal isn't to \"beat\" Gmail—it's simply to avoid robotic timing and create a smoother workflow.\n\nDesigning the License System\n\nI didn't want all business logic inside the extension.\n\nInstead, the backend validates every license.\n\nThe Flask API checks:\n\nLicense validity\nActivated machine\nDaily limits\nRemaining quota\nExpiration status\n\nThis keeps sensitive logic off the client side.\n\nWhy Use Flask?\n\nI considered several backend frameworks.\n\nFlask won because it offers:\n\nMinimal setup\nFast development\nExcellent documentation\nEasy REST API creation\nGreat Python ecosystem\n\nFor a lightweight licensing server, it was exactly what I needed.\n\nKeeping the Backend Simple\n\nThe database is intentionally straightforward.\n\nExample tables include:\n\nLicenses\nActivations\nGmail usage\nAdmin users\n\nSimple schemas are often easier to maintain than highly complex designs.\n\nProblems I Didn't Expect\n\nBuilding browser automation introduced challenges I hadn't anticipated.\n\nSome examples:\n\nGmail UI Changes\n\nSmall interface updates can break selectors.\n\nBuilding resilient selectors became essential.\n\nAsynchronous Timing\n\nBrowser automation depends heavily on waiting for elements.\n\nToo early?\n\nThe element doesn't exist.\n\nToo late?\n\nThe workflow becomes slow.\n\nFinding the right balance required many iterations.\n\nExtension State\n\nManaging state across popup scripts, background scripts, and content scripts was harder than expected.\n\nKeeping everything synchronized became one of the biggest engineering tasks.\n\nWhat I Learned\n\nA few lessons stand out.\n\nKeep State in One Place\n\nDuplicated state causes bugs.\n\nUsing a single source of truth simplified debugging dramatically.\n\nLog Everything\n\nDetailed logging reduced debugging time from hours to minutes.\n\nExpect UI Changes\n\nIf you're automating websites, assume they'll change.\n\nDesign your automation to fail gracefully.\n\nBuild for Recovery\n\nUsers will eventually close their browser, lose internet connectivity, or restart their computer.\n\nRecovery is more important than perfection.\n\nFuture Improvements\n\nThe project is still evolving.\n\nSome features I'm exploring include:\n\nBetter campaign analytics\nImproved scheduling\nMore reporting options\nEnhanced dashboard metrics\nAdditional workflow automation\n\nEvery version teaches something new.\n\nFinal Thoughts\n\nBuilding browser automation software has been one of the most rewarding projects I've worked on.\n\nIt combines frontend development, backend APIs, browser internals, state management, deployment, and user experience into a single product.\n\nIf you're interested in Chrome extension development, my advice is simple:\n\nStart with a small feature.\n\nShip it.\n\nTest it with real users.\n\nThen improve it one iteration at a time.\n\nThat's exactly how this project has grown.\n\nIf you'd like to see the project that inspired this article, you can explore TinyTask Mailer Pro and other productivity tools at https://tinytask.org.\n\nI'm also interested in hearing how others approach browser automation. What challenges have you run into while building Chrome extensions?",
  "title": "How I Built a Gmail Automation Chrome Extension Using JavaScript and Flask"
}