<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Archit Mittal</title>
    <description>The latest articles on DEV Community by Archit Mittal (@automate-archit).</description>
    <link>https://dev.to/automate-archit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F583571%2Fc2c0b665-b457-4281-a519-d6e76245e21f.png</url>
      <title>DEV Community: Archit Mittal</title>
      <link>https://dev.to/automate-archit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/automate-archit"/>
    <language>en</language>
    <item>
      <title>Build a Desktop File Organizer in 35 Lines of Python — Weekend Project</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Sat, 11 Apr 2026 04:50:20 +0000</pubDate>
      <link>https://dev.to/automate-archit/build-a-desktop-file-organizer-in-35-lines-of-python-weekend-project-3o4h</link>
      <guid>https://dev.to/automate-archit/build-a-desktop-file-organizer-in-35-lines-of-python-weekend-project-3o4h</guid>
      <description>&lt;p&gt;Your Downloads folder is a mess. Mine was too — 400+ files, zero organization. So I spent a Saturday afternoon writing a Python script that watches a folder and auto-sorts files into subfolders by type. 35 lines, no external libraries beyond watchdog, runs forever in the background.&lt;/p&gt;

&lt;p&gt;Here's the full weekend project. You'll have it running in under 30 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build This?
&lt;/h2&gt;

&lt;p&gt;Every developer I know has a chaotic Downloads folder. PDFs from 2019 sitting next to screenshots from yesterday. You could manually sort them — or you could automate it once and never think about it again.&lt;/p&gt;

&lt;p&gt;This project teaches you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File system operations with &lt;code&gt;os&lt;/code&gt; and &lt;code&gt;shutil&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;watchdog&lt;/code&gt; library for real-time file monitoring&lt;/li&gt;
&lt;li&gt;How to build a script that runs as a background service&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;First, install the only dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;watchdog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now here's the complete script — &lt;code&gt;file_organizer.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;watchdog.observers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Observer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;watchdog.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileSystemEventHandler&lt;/span&gt;

&lt;span class="c1"&gt;# Configure your folder path and file type mappings
&lt;/span&gt;&lt;span class="n"&gt;WATCH_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;~/Downloads&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;FILE_TYPES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.jpg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.jpeg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.gif&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.svg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.webp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Documents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.docx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.xlsx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.pptx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Videos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.mp4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.mkv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.avi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.mov&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Audio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.mp3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.wav&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.flac&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.aac&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Archives&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.zip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.rar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.tar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.gz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.7z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.js&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.css&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.sql&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Installers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.dmg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.exe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.msi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.deb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.pkg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_destination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;FILE_TYPES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;folder&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Other&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SortHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# wait for download to finish
&lt;/span&gt;        &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;src_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;dest_folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_destination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;dest_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WATCH_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest_folder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makedirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;src_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Moved: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dest_folder&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error moving &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Observer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SortHandler&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;WATCH_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recursive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Watching &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;WATCH_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; for new files...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The mapping dictionary&lt;/strong&gt; (&lt;code&gt;FILE_TYPES&lt;/code&gt;) maps folder names to file extensions. When a new file arrives, the script checks its extension and picks the right folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;get_destination()&lt;/code&gt;&lt;/strong&gt; loops through your mappings and returns the folder name. If the extension doesn't match anything, it goes to "Other" — nothing gets lost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;SortHandler&lt;/code&gt;&lt;/strong&gt; extends watchdog's &lt;code&gt;FileSystemEventHandler&lt;/code&gt;. The &lt;code&gt;on_created&lt;/code&gt; method fires every time a new file appears. The 1-second sleep is important — it prevents the script from moving a file that's still downloading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The main block&lt;/strong&gt; creates an &lt;code&gt;Observer&lt;/code&gt; that watches your Downloads folder. It runs in an infinite loop until you press &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make It Your Own
&lt;/h2&gt;

&lt;p&gt;Here are 5 ways to extend this project:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Add date-based subfolders:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_date_folder&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%Y-%m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Creates folders like "Documents/2026-04/"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Handle duplicate filenames:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;safe_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Add desktop notifications (macOS):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;osascript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-e&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;display notification &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; with title &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Log everything to a file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;organizer.log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Moved: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dest_folder&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Run it on startup&lt;/strong&gt; — Add it to your crontab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# crontab -e&lt;/span&gt;
@reboot python3 /path/to/file_organizer.py &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real Numbers From My Setup
&lt;/h2&gt;

&lt;p&gt;I've been running this on my machine for 3 months:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1,200+ files&lt;/strong&gt; auto-sorted without lifting a finger&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downloads folder&lt;/strong&gt; went from 400 files to 7 organized subfolders&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time saved&lt;/strong&gt;: roughly 15 minutes per week of manual sorting&lt;/li&gt;
&lt;li&gt;At a modest consulting rate of ₹1,500/hr, that's &lt;strong&gt;₹6,000/month&lt;/strong&gt; in recovered time&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Bigger Lesson
&lt;/h2&gt;

&lt;p&gt;This is a 30-minute project, but it teaches you a pattern that scales. The same watchdog + handler architecture works for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-uploading invoices to Google Drive&lt;/li&gt;
&lt;li&gt;Parsing incoming CSVs and loading them into a database&lt;/li&gt;
&lt;li&gt;Monitoring log files for error patterns&lt;/li&gt;
&lt;li&gt;Triggering alerts when specific files land in a shared folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start small. Automate one annoying thing this weekend. Then next weekend, automate another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run It Now
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;watchdog
&lt;span class="c"&gt;# Save the script above as file_organizer.py&lt;/span&gt;
python file_organizer.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or just copy-paste the script above. It works out of the box.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Archit Mittal — I automate chaos for businesses. Follow me for daily automation content.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>automation</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Built an Automated WhatsApp Invoice System for My Client in 3 Hours</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Thu, 09 Apr 2026 04:46:34 +0000</pubDate>
      <link>https://dev.to/automate-archit/how-i-built-an-automated-whatsapp-invoice-system-for-my-client-in-3-hours-28dd</link>
      <guid>https://dev.to/automate-archit/how-i-built-an-automated-whatsapp-invoice-system-for-my-client-in-3-hours-28dd</guid>
      <description>&lt;p&gt;Last week, a CA firm in Jaipur reached out to me with a painfully familiar problem: their accountant was manually creating invoices in Excel, converting them to PDF, and sending them on WhatsApp — one by one — to 200+ clients every month.&lt;/p&gt;

&lt;p&gt;Time spent? About 3 full working days. Every. Single. Month.&lt;/p&gt;

&lt;p&gt;I built them an automated system in 3 hours flat. Here's exactly how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The firm's workflow looked like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Excel template&lt;/li&gt;
&lt;li&gt;Fill in client name, GSTIN, amounts, due dates&lt;/li&gt;
&lt;li&gt;Save as PDF&lt;/li&gt;
&lt;li&gt;Open WhatsApp Web&lt;/li&gt;
&lt;li&gt;Search for the client&lt;/li&gt;
&lt;li&gt;Attach PDF, type a message, hit send&lt;/li&gt;
&lt;li&gt;Repeat 200 times&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The accountant was burning ~24 hours/month on this. At ₹300/hour, that's ₹7,200/month — or ₹86,400/year — just on invoice delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Python + WhatsApp Business API
&lt;/h2&gt;

&lt;p&gt;I broke the automation into 3 parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Invoice generation&lt;/strong&gt; from a Google Sheet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDF creation&lt;/strong&gt; using a template&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WhatsApp delivery&lt;/strong&gt; via the Business API&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Part 1: Pull Data from Google Sheets
&lt;/h3&gt;

&lt;p&gt;First, I set up a Google Sheet as the single source of truth. The accountant just fills in the data — the script does the rest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;gspread&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.oauth2.service_account&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Credentials&lt;/span&gt;

&lt;span class="n"&gt;SCOPES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://www.googleapis.com/auth/spreadsheets.readonly&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_service_account_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;creds.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scopes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SCOPES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gspread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Monthly_Invoices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;worksheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;April2026&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_all_records&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Each record has: client_name, gstin, amount, due_date, phone
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; invoices to process&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us a clean list of dictionaries — one per client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: Generate PDF Invoices
&lt;/h3&gt;

&lt;p&gt;I used &lt;code&gt;reportlab&lt;/code&gt; for PDF generation. The firm wanted their letterhead, so I created a reusable template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;reportlab.lib.pagesizes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;A4&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;reportlab.pdfgen&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;reportlab.lib.units&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;inch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invoices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makedirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;client_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%B_%Y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pagesize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;A4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A4&lt;/span&gt;

    &lt;span class="c1"&gt;# Header
&lt;/span&gt;    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helvetica-Bold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TAX INVOICE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Firm details
&lt;/span&gt;    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helvetica&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sharma &amp;amp; Associates, Chartered Accountants&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.7&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GSTIN: 08AXXXX1234X1Z5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Client details
&lt;/span&gt;    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helvetica-Bold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bill To: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;client_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helvetica&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;2.8&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GSTIN: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gstin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Invoice details
&lt;/span&gt;    &lt;span class="n"&gt;inv_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INV-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y%m&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;client_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invoice #: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;inv_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;2.8&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Date: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%d-%m-%Y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;3.1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Due Date: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;due_date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Amount table
&lt;/span&gt;    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;gst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.18&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gst&lt;/span&gt;

    &lt;span class="n"&gt;y_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helvetica-Bold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Amount (₹)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helvetica&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pos&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Professional Services&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pos&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pos&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GST @ 18%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pos&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gst&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helvetica-Bold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pos&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TOTAL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pos&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;₹&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Bank details
&lt;/span&gt;    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Helvetica&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;inch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bank: HDFC Bank | A/C: XXXX1234 | IFSC: HDFC0001234&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;

&lt;span class="c1"&gt;# Generate all invoices
&lt;/span&gt;&lt;span class="n"&gt;invoice_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;invoice_files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;phone&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;client_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Generated invoice for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;client_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Part 3: Send via WhatsApp Business API
&lt;/h3&gt;

&lt;p&gt;This is where the magic happens. I used the WhatsApp Business API (via a BSP provider — costs about ₹0.50 per message in India).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;WHATSAPP_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PHONE_NUMBER_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_phone_number_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Upload PDF to WhatsApp media endpoint&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.facebook.com/v18.0/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PHONE_NUMBER_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/media&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;WHATSAPP_TOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/pdf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;messaging_product&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;whatsapp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;media_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Send invoice PDF with a message via WhatsApp&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.facebook.com/v18.0/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PHONE_NUMBER_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;WHATSAPP_TOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messaging_product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whatsapp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;91&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;media_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filename&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invoice_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;client_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;caption&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;client_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please find attached your invoice for the amount &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;of Rs.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (inclusive of GST).&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Kindly process the payment by the due date.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Regards,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Sharma &amp;amp; Associates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Send all invoices
&lt;/span&gt;&lt;span class="n"&gt;sent_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;invoice_files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;media_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;upload_media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;send_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;media_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sent_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sent to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Rate limiting
&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Done! Sent: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sent_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | Failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed clients: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;I wrapped everything in a single &lt;code&gt;run.py&lt;/code&gt; script with logging and error handling. The accountant now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Updates the Google Sheet with this month's data&lt;/li&gt;
&lt;li&gt;Runs &lt;code&gt;python run.py&lt;/code&gt; (or I set up a cron job for the 1st of every month)&lt;/li&gt;
&lt;li&gt;Gets a summary in the terminal showing what was sent&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total execution time for 200 invoices: &lt;strong&gt;under 8 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time per month&lt;/td&gt;
&lt;td&gt;~24 hours&lt;/td&gt;
&lt;td&gt;~8 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per month&lt;/td&gt;
&lt;td&gt;₹7,200 (labor)&lt;/td&gt;
&lt;td&gt;₹100 (API costs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Errors (wrong client)&lt;/td&gt;
&lt;td&gt;2-3/month&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client complaints&lt;/td&gt;
&lt;td&gt;Frequent delays&lt;/td&gt;
&lt;td&gt;Same-day delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Annual savings: ₹85,200 in labor + eliminated errors.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The firm paid me ₹15,000 for this build — ROI in less than 2 months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Lessons
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with the spreadsheet.&lt;/strong&gt; Every automation project I take on starts with understanding the data source. If the data is messy, fix that first. The actual automation is the easy part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use APIs, not UI bots.&lt;/strong&gt; I could have used browser automation to control WhatsApp Web, but that breaks every time WhatsApp updates their UI. The Business API is stable, fast, and gives you delivery receipts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build for the person who runs it.&lt;/strong&gt; The accountant isn't a developer. So I made the Google Sheet the only interface they touch. No terminal, no config files, no "just edit this JSON."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Charge for value, not hours.&lt;/strong&gt; This took me 3 hours. If I charged hourly at ₹1,000/hr, that's ₹3,000. But I saved them ₹85,000/year. ₹15,000 is a no-brainer for the client and fair compensation for the value delivered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to Build Something Similar?
&lt;/h2&gt;

&lt;p&gt;Here's the minimal setup you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.8+&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gspread&lt;/code&gt; and &lt;code&gt;google-auth&lt;/code&gt; for Sheets access&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reportlab&lt;/code&gt; for PDF generation&lt;/li&gt;
&lt;li&gt;WhatsApp Business API access (apply through Meta Business)&lt;/li&gt;
&lt;li&gt;A Google Cloud service account with Sheets API enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install everything with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;gspread google-auth reportlab requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire codebase is under 200 lines. You could build this in an afternoon.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Archit Mittal — I automate chaos for businesses. Follow me for daily automation content.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>automation</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Had My First Automated Trade Running in 47 Minutes — Here's the Exact Setup</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Wed, 08 Apr 2026 12:58:21 +0000</pubDate>
      <link>https://dev.to/automate-archit/i-had-my-first-automated-trade-running-in-47-minutes-heres-the-exact-setup-3g79</link>
      <guid>https://dev.to/automate-archit/i-had-my-first-automated-trade-running-in-47-minutes-heres-the-exact-setup-3g79</guid>
      <description>&lt;p&gt;A friend called me last week with a familiar problem.&lt;/p&gt;

&lt;p&gt;He had been comparing broker API documentation for three days straight. Zerodha versus Fyers versus Upstox versus Angel One. Spreadsheets of feature comparisons. Reddit threads. YouTube reviews. Three days of research, zero lines of code written.&lt;/p&gt;

&lt;p&gt;I told him to stop comparing and start building.&lt;/p&gt;

&lt;p&gt;By that evening, he had his first automated trade running. A simple bot that auto-squares-off all open positions at 3:15 PM so he never forgets to close before market end. Nothing fancy. But it worked. And it replaced a task that had cost him real money twice in the previous month when he forgot to exit positions manually.&lt;/p&gt;

&lt;p&gt;This article is the guide I wish existed when I started automating trades two years ago. No theory. No affiliate links. Just the practical breakdown of how to go from zero to a working trading bot using Python and a broker API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Most People Never Get Past the Research Phase
&lt;/h2&gt;

&lt;p&gt;Here is the pattern I see repeatedly. Someone decides they want to automate their trading. They spend a week reading documentation. They get overwhelmed by the differences between brokers. They bookmark fifteen tutorials. They never write a single line of code.&lt;/p&gt;

&lt;p&gt;The problem is not complexity. The problem is that most guides start with the wrong question. They ask "which broker has the best API?" when they should ask "which broker will let me run my first bot today?"&lt;/p&gt;

&lt;p&gt;Those are very different questions.&lt;/p&gt;

&lt;p&gt;The best API on paper means nothing if the authentication flow takes you a week to figure out. The most feature-rich documentation is useless if the sandbox environment does not match production. I have seen developers with years of experience get stuck for days on OAuth token refresh flows that should take twenty minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup I Actually Use (And Why)
&lt;/h2&gt;

&lt;p&gt;I run four trading bots simultaneously on Fyers API. Here is why I chose it, with specific technical details rather than marketing language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication that does not fight you.&lt;/strong&gt; Fyers uses a straightforward OAuth2 flow. You get an app_id and secret_key from their developer portal. The access token generation takes one API call. Compare this to brokers where you need to handle TOTP-based two-factor authentication programmatically — that alone can eat an entire afternoon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One API for everything you need.&lt;/strong&gt; Historical data, real-time websocket streaming, order placement, and position management — all through a single REST API with consistent response formats. Some brokers split these across different endpoints with different authentication methods. That means maintaining multiple connection handlers in your code. With a unified API, your codebase stays clean.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate limits that make sense for retail.&lt;/strong&gt; Ten requests per second. If you are a retail algo trader running a few strategies, that is more than enough. I run four bots simultaneously and have never hit the ceiling. For context, even a high-frequency scalping strategy at the retail level rarely needs more than two to three requests per second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero cost for API access.&lt;/strong&gt; You pay standard brokerage on executed trades. The API itself costs nothing. No monthly subscription. No per-call charges. No premium tier you need to unlock to access historical data. This matters more than people realise — I have seen traders pay $50 to $100 per month for API access on international platforms before they have even validated whether their strategy works.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Zero to First Bot: The 47-Minute Breakdown
&lt;/h2&gt;

&lt;p&gt;Here is exactly how the timeline broke down when I set up my friend that evening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minutes 0 to 8: Developer portal setup.&lt;/strong&gt; Created an app on Fyers API developer portal. Got the app_id and secret_key. This is a one-time setup — you reuse these credentials for every bot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minutes 8 to 15: Authentication script.&lt;/strong&gt; Wrote a Python script to generate the access token. The Fyers Python SDK (fyers-apiv3) handles most of the heavy lifting. Install it, pass your credentials, get a token. Seven minutes including pip install time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minutes 15 to 30: The bot logic.&lt;/strong&gt; His requirement was simple — check all open positions at 3:15 PM and square them off. That is a position fetch call followed by conditional sell orders. The logic itself was about 30 lines of Python. We wrapped it in a scheduled function using Python's schedule library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minutes 30 to 40: Testing with paper trades.&lt;/strong&gt; Fyers allows you to validate order parameters without actually executing. We tested the flow end to end — fetch positions, generate square-off orders, validate them. Fixed two bugs: one where we were not handling the case of zero open positions, and another where the order type parameter was wrong for market orders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minutes 40 to 47: Live deployment.&lt;/strong&gt; Switched from validation mode to live execution. Ran the bot. Watched it sit idle until 3:15 PM on the next trading day, then cleanly close all three open positions.&lt;/p&gt;

&lt;p&gt;Total code: under 60 lines of Python. No frameworks. No Docker containers. No cloud deployment. Just a Python script running on his laptop with a cron job.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bots I Run Daily (And What They Actually Do)
&lt;/h2&gt;

&lt;p&gt;Over two years, I have built up to four bots running simultaneously. None of them are complex. The power is in consistency, not sophistication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bot 1: The Position Closer.&lt;/strong&gt; Squares off all positions at 3:15 PM. This was the first bot I ever wrote. It has saved me from forgetting to exit positions at least a dozen times. At an average of $200 to $500 per forgotten exit (in slippage and overnight risk), this single bot has probably saved me $3,000 or more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bot 2: The Morning Screener.&lt;/strong&gt; Runs at 9:16 AM (one minute after market open). Scans a predefined watchlist for stocks that gapped up or down more than two percent from previous close. Sends results to my phone via WhatsApp using a simple webhook. I review the list over my morning coffee instead of staring at a screen for twenty minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bot 3: The SIP Optimizer.&lt;/strong&gt; This one runs weekly. Instead of fixed-date SIP investments, it checks the Nifty P/E ratio. If the market is trading below historical average P/E, it invests more. If above average, it invests less. A basic value-averaging approach that anyone with a mutual fund account thinks about but nobody does manually because it is tedious. The total difference over eighteen months has been a 2.3% higher return compared to my fixed-date SIP — which translates to roughly $400 extra on a moderate portfolio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bot 4: The Trade Journal.&lt;/strong&gt; Every executed trade gets logged automatically into a Google Sheet with entry price, exit price, P&amp;amp;L, holding duration, and the strategy tag. At the end of the week, a summary lands in my WhatsApp. I used to maintain this journal manually and abandoned it after two weeks every time. The automated version has been running for fourteen months without a single missed entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mistakes That Cost Me Real Money
&lt;/h2&gt;

&lt;p&gt;I would be dishonest if I presented this as a smooth journey. Here are the expensive lessons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: Not handling token expiry.&lt;/strong&gt; Fyers access tokens expire daily. My first bot worked perfectly for one day and then silently failed the next morning because the token had expired. I only noticed when I checked my positions at 3:30 PM and found them still open. Cost: about $180 in overnight slippage. The fix took ten minutes — a token refresh function that runs at 8:30 AM before market open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2: No circuit breaker.&lt;/strong&gt; My screener bot once sent me forty-seven WhatsApp messages in three minutes during a market-wide gap event. The fix: a simple counter that caps alerts at five per run and aggregates the rest into a single summary message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3: Trusting backtests blindly.&lt;/strong&gt; I built a momentum strategy that looked incredible in backtesting — 34% annual returns over five years of historical data. In live trading, it lost 8% in the first month. The backtest did not account for slippage, partial fills, or the fact that the stocks it picked were often illiquid. I shut it down, went back to simple automation, and have not tried to build a "strategy" bot since. My bots automate actions, not decisions. That distinction matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  When You Should NOT Automate Your Trading
&lt;/h2&gt;

&lt;p&gt;This might be the most important section of this article.&lt;/p&gt;

&lt;p&gt;Do not automate your trading strategy if you have not traded it manually for at least three months. Automation amplifies whatever you feed it. If your manual strategy loses money, your automated strategy will lose money faster and more consistently.&lt;/p&gt;

&lt;p&gt;Do not automate if you cannot explain every line of your bot's code. I have seen people copy trading bots from GitHub, run them with real money, and then panic when something unexpected happens. If you did not write it, you do not understand it. If you do not understand it, you should not trust it with your money.&lt;/p&gt;

&lt;p&gt;Do not start with strategy automation. Start with task automation — the boring stuff like position closing, journaling, and screening. These have near-zero financial risk and teach you the entire API workflow. Once you are comfortable with the plumbing, then consider automating decisions. Most people do this backwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started Today
&lt;/h2&gt;

&lt;p&gt;If you have basic Python knowledge and a trading account, you can have your first bot running this evening. Here is the minimal path.&lt;/p&gt;

&lt;p&gt;First, pick one repetitive task you do every trading day. Not a strategy. A task. Closing positions, checking prices, logging trades — something mechanical.&lt;/p&gt;

&lt;p&gt;Second, set up API access with your broker. If you are in India, Fyers is my recommendation. If you are international, Interactive Brokers or Alpaca both have solid Python SDKs.&lt;/p&gt;

&lt;p&gt;Third, write the smallest possible script that does that one task. Do not add features. Do not handle edge cases yet. Get the core loop working.&lt;/p&gt;

&lt;p&gt;Fourth, run it in paper or validation mode for one full week. Watch it. Log what it does. Fix what breaks.&lt;/p&gt;

&lt;p&gt;Fifth, go live with small positions. Scale up only after you trust it.&lt;/p&gt;

&lt;p&gt;The entire process should take one weekend if you focus. Not three days of comparison shopping. One weekend of building.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Value Is Not the Trades
&lt;/h2&gt;

&lt;p&gt;After two years of running trading bots, here is what I have learned: the money saved from automation is real but it is not the main value. The real value is in the discipline it enforces.&lt;/p&gt;

&lt;p&gt;My trade journal has fourteen months of unbroken data because a bot fills it in. My SIP adjustments happen consistently because a script makes them. My positions close on time because code does not forget.&lt;/p&gt;

&lt;p&gt;Automation does not make you a better trader. It makes you a more consistent one. And in markets, consistency compounds.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Archit Mittal is the founder of Automate Algos. He helps businesses automate chaos using AI agents and custom workflows. Connect with him on LinkedIn &lt;a href="https://linkedin.com/in/automate-archit" rel="noopener noreferrer"&gt;@automate-archit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>automation</category>
      <category>fintech</category>
      <category>trading</category>
    </item>
    <item>
      <title>How I Automated Invoice Processing for a ₹2Cr/Year Business in 3 Days</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Wed, 08 Apr 2026 04:48:53 +0000</pubDate>
      <link>https://dev.to/automate-archit/how-i-automated-invoice-processing-for-a-2cryear-business-in-3-days-54pm</link>
      <guid>https://dev.to/automate-archit/how-i-automated-invoice-processing-for-a-2cryear-business-in-3-days-54pm</guid>
      <description>&lt;p&gt;Last month, a textile exporter in Surat reached out to me with a familiar problem: their accounts team of 4 people was spending 6+ hours every day manually matching invoices to purchase orders and bank statements. For a business doing ₹2 crore in annual revenue with 200+ invoices per month, this was eating into their margins badly.&lt;/p&gt;

&lt;p&gt;Here's how I automated their entire invoice processing pipeline in just 3 days using Python — and brought that 6-hour daily task down to 15 minutes of human review.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Death by Manual Data Entry
&lt;/h2&gt;

&lt;p&gt;Their workflow looked like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive invoices via email (PDF attachments) and WhatsApp&lt;/li&gt;
&lt;li&gt;Manually type invoice details into a Google Sheet&lt;/li&gt;
&lt;li&gt;Cross-check each invoice against purchase orders in another sheet&lt;/li&gt;
&lt;li&gt;Match payments in their bank statement CSV to invoices&lt;/li&gt;
&lt;li&gt;Flag mismatches and follow up with vendors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The error rate was roughly 8-12% — wrong amounts, missed invoices, duplicate entries. Each error meant hours of back-and-forth with vendors and delayed payments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost of the old process:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4 staff × 6 hours/day × ₹15,000/month salary = ₹60,000/month just on invoice processing&lt;/li&gt;
&lt;li&gt;Late payment penalties averaging ₹25,000/month&lt;/li&gt;
&lt;li&gt;Total: ~₹85,000/month or ₹10.2 lakh/year&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: A 3-Layer Python Pipeline
&lt;/h2&gt;

&lt;p&gt;I built a pipeline with three components: Extract, Match, and Report.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 1: Invoice Data Extraction
&lt;/h3&gt;

&lt;p&gt;First, I needed to pull structured data from PDF invoices. I used &lt;code&gt;pdfplumber&lt;/code&gt; for text-based PDFs and &lt;code&gt;pytesseract&lt;/code&gt; for scanned ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pdfplumber&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;

&lt;span class="nd"&gt;@dataclassclass&lt;/span&gt; &lt;span class="n"&gt;InvoiceData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;invoice_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;vendor_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;total_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;gst_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_invoice_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;InvoiceData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pdfplumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;full_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;full_text&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;

    &lt;span class="c1"&gt;# Extract key fields using regex patterns
&lt;/span&gt;    &lt;span class="n"&gt;inv_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Invoice\s*#?\s*:?\s*([A-Z0-9-]+)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;full_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Total\s*:?\s*₹?\s*([\d,]+\.?\d*)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;full_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GST\s*:?\s*₹?\s*([\d,]+\.?\d*)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;full_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;InvoiceData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;invoice_number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inv_number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;inv_number&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNKNOWN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;vendor_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;extract_vendor_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;extract_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;total_amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;gst_amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gst&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;line_items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;extract_line_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the 30% of invoices that were scanned images, I added an OCR fallback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytesseract&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pdf2image&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;convert_from_path&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ocr_extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convert_from_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dpi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;pytesseract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;image_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eng+hin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Hindi language support was critical since many local vendors send bilingual invoices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 2: Smart Matching Engine
&lt;/h3&gt;

&lt;p&gt;The matching logic compares extracted invoice data against purchase orders and bank transactions. I used fuzzy matching because vendor names are never consistent (think "Raj Textiles" vs "Raj Textile Pvt Ltd").&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fuzzywuzzy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;fuzz&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;match_invoice_to_po&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InvoiceData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;po_df&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;best_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;best_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;po&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;po_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterrows&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# Fuzzy match on vendor name
&lt;/span&gt;        &lt;span class="n"&gt;name_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fuzz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token_sort_ratio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vendor_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
            &lt;span class="n"&gt;po&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vendor_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Exact or close match on amount (within 1% for rounding)
&lt;/span&gt;        &lt;span class="n"&gt;amount_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_amount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;po&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;po&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;amount_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;amount_diff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;amount_diff&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Combined weighted score
&lt;/span&gt;        &lt;span class="n"&gt;combined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name_score&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount_score&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;combined&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;best_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;best_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;combined&lt;/span&gt;
            &lt;span class="n"&gt;best_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;po&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;po_number&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;best_match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;po_number&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;best_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;best_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;matched&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;best_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;review&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;best_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unmatched&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;match_to_bank_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InvoiceData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bank_df&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Match invoice to bank transactions within a 7-day window&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;txn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bank_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterrows&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;amount_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
        &lt;span class="n"&gt;date_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;amount_match&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;date_match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;txn_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;txn&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reference&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;paid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;txn_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unpaid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Day 3: Email Monitoring + Dashboard
&lt;/h3&gt;

&lt;p&gt;I set up an email listener to auto-download invoice attachments and a simple Streamlit dashboard for the accounts team:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;imaplib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_invoice_emails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;imap_server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;download_dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./invoices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;mail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imaplib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IMAP4_SSL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imap_server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inbox&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Search for emails with PDF attachments from last 24 hours
&lt;/span&gt;    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(SINCE &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;08-Apr-2026&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; SUBJECT &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invoice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;download_dir&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;downloaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;msg_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(RFC822)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message_from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_content_type&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/pdf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_filename&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;filepath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;download_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;downloaded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;downloaded&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Results: ₹8.5 Lakh Annual Savings
&lt;/h2&gt;

&lt;p&gt;After one month of running the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Processing time&lt;/strong&gt;: 6 hours/day → 15 minutes of human review&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error rate&lt;/strong&gt;: 12% → 0.5% (only edge cases need manual intervention)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Staff reallocation&lt;/strong&gt;: 3 of 4 accounts staff moved to higher-value work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Late payment penalties&lt;/strong&gt;: ₹25,000/month → ₹2,000/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Net savings&lt;/strong&gt;: ~₹8.5 lakh/year&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The total cost of building this? About ₹45,000 for my 3 days of work plus ₹500/month for a basic VPS to run the pipeline. ROI hit positive in the first month itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways for Indian Businesses
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with the most painful manual process&lt;/strong&gt; — invoice matching was their biggest time sink, not the fanciest problem to solve&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build for bilingual&lt;/strong&gt; — if you're automating for Indian businesses, Hindi/regional language support isn't optional. &lt;code&gt;pytesseract&lt;/code&gt; with language packs handles this well&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep humans in the loop&lt;/strong&gt; — the system flags low-confidence matches for review instead of auto-approving everything. This built trust with the accounts team&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use fuzzy matching liberally&lt;/strong&gt; — Indian business names have many variations. Hard string matching will miss 40%+ of valid matches&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Measure in ₹, not in hours&lt;/strong&gt; — when I told the owner "you'll save ₹8.5 lakh/year," the approval was instant. Saying "you'll save 1,400 hours" wouldn't have had the same impact&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Want to Build Something Similar?
&lt;/h2&gt;

&lt;p&gt;The full pipeline is about 400 lines of Python. The key libraries you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pdfplumber&lt;/code&gt; — PDF text extraction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pytesseract&lt;/code&gt; + &lt;code&gt;pdf2image&lt;/code&gt; — OCR for scanned documents&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fuzzywuzzy&lt;/code&gt; — fuzzy string matching&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pandas&lt;/code&gt; — data manipulation and CSV handling&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;streamlit&lt;/code&gt; — quick dashboard for the review interface&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;imaplib&lt;/code&gt; — email monitoring (built into Python)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The entire stack runs on a ₹500/month DigitalOcean droplet. No expensive SaaS subscriptions needed.&lt;/p&gt;




&lt;p&gt;*I'm Archit Mittal — I automate chaos for businesses. Follow me for daily automation content.*thon)&lt;/p&gt;

&lt;p&gt;The entire stack runs on a ₹500/month DigitalOcean droplet. No expensive SaaS subscriptions needed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Archit Mittal — I automate chaos for businesses. Follow me for daily automation content.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Vibe Coding Saved My Client ₹2.4 Lakh — By Replacing a Junior Developer's Typing, Not Their Job</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Wed, 01 Apr 2026 12:55:02 +0000</pubDate>
      <link>https://dev.to/automate-archit/vibe-coding-saved-my-client-24-lakh-by-replacing-a-junior-developers-typing-not-their-job-cjg</link>
      <guid>https://dev.to/automate-archit/vibe-coding-saved-my-client-24-lakh-by-replacing-a-junior-developers-typing-not-their-job-cjg</guid>
      <description>&lt;p&gt;A client called me last month with a hiring problem.&lt;/p&gt;

&lt;p&gt;His senior developer was drowning. Every new feature followed the same painful loop — write detailed specs, hand them to the junior dev, wait through three review cycles, fix the mistakes, and finally ship two weeks later. The junior developer was essentially a spec-to-code translator. Necessary, but slow.&lt;/p&gt;

&lt;p&gt;He was about to post a job listing for a second junior developer. Salary: ₹2.4 lakh for six months. Plus onboarding time. Plus the senior dev spending 30% of their day reviewing beginner-level code.&lt;/p&gt;

&lt;p&gt;I told him to wait one weekend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Happened That Weekend
&lt;/h2&gt;

&lt;p&gt;The senior developer spent Saturday and Sunday learning vibe coding with Claude Code — Anthropic's terminal-based AI coding assistant. Not watching tutorials. Not reading documentation for hours. Just building.&lt;/p&gt;

&lt;p&gt;By Monday, the workflow had completely transformed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before vibe coding:&lt;/strong&gt; Senior dev writes a two-page spec document. Junior dev interprets it (sometimes incorrectly). First draft comes back in 3-4 days. Two rounds of review. Bug fixes. Ship date: 10-14 days per feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After vibe coding:&lt;/strong&gt; Senior dev describes the feature to Claude Code in plain English. Reviews the output. Tweaks the architecture decisions. Ships in 1-2 days.&lt;/p&gt;

&lt;p&gt;The same person. The same codebase. The same features. Just a fundamentally different process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Here is where most "AI will replace developers" articles get it dangerously wrong.&lt;/p&gt;

&lt;p&gt;That senior developer did not stop thinking. He did not blindly accept whatever Claude Code generated. In fact, his job got harder in some ways — because now he was reviewing AI-generated code at the speed of AI generation, not at the comfortable pace of a junior developer's output.&lt;/p&gt;

&lt;p&gt;Every single line still got reviewed. Bugs that Claude Code missed — and it does miss them — still got caught by a human brain with years of context about the codebase. Architecture decisions that require understanding the business, the users, and the technical debt? Still 100% human.&lt;/p&gt;

&lt;p&gt;Vibe coding replaced the junior developer's typing. It did not replace the senior developer's thinking. That distinction matters more than any benchmark or demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Changes the Hiring Equation in India
&lt;/h2&gt;

&lt;p&gt;The Indian tech market has a specific pattern. Companies hire junior developers at ₹15,000 to ₹40,000 per month primarily as code translators — someone who takes specs and turns them into working code. The actual engineering decisions happen one level up.&lt;/p&gt;

&lt;p&gt;Vibe coding disrupts this exact layer. When a senior developer can describe a feature in natural language and get working code back in minutes, the spec-to-code translation role shrinks dramatically.&lt;/p&gt;

&lt;p&gt;This does not mean junior developers become irrelevant. It means the definition of "junior developer" is changing. The ones who will thrive are those who learn to work alongside AI tools early — who treat Claude Code or similar tools as a pair programming partner rather than a threat.&lt;/p&gt;

&lt;p&gt;My client's senior developer now ships features at roughly 5x the previous pace. Not because the code quality dropped, but because the iteration loop collapsed. Describe, review, tweak, ship. No waiting for someone else to interpret your vision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers That Matter
&lt;/h2&gt;

&lt;p&gt;Let me break down what ₹2.4 lakh over six months actually looks like when you do the math on vibe coding instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost of hiring a junior developer:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Salary for 6 months: ₹2,40,000 (at ₹40,000/month)&lt;/li&gt;
&lt;li&gt;Onboarding time: 2-3 weeks of the senior dev's bandwidth&lt;/li&gt;
&lt;li&gt;Review overhead: approximately 30% of senior dev's daily time&lt;/li&gt;
&lt;li&gt;Feature delivery speed: 10-14 days per feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cost of vibe coding with Claude Code:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code API usage: roughly ₹3,000 to ₹5,000 per month depending on volume&lt;/li&gt;
&lt;li&gt;Learning curve: 1 weekend&lt;/li&gt;
&lt;li&gt;Review overhead: still exists, but at the senior dev's pace, not the junior's&lt;/li&gt;
&lt;li&gt;Feature delivery speed: 1-2 days per feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The raw savings on salary alone: over ₹2 lakh in six months. But the real value is in the speed multiplier. Features that took two weeks now take two days. That compounds over an entire product roadmap.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Vibe Coding Does Not Work
&lt;/h2&gt;

&lt;p&gt;I would be dishonest if I painted this as a universal solution. Vibe coding falls apart in specific situations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complex system architecture&lt;/strong&gt; — Claude Code can write individual features well, but it does not understand how your microservices talk to each other across three years of accumulated technical debt. A human architect is irreplaceable here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Domain-specific logic&lt;/strong&gt; — If you are building trading algorithms, medical software, or anything where the business logic is the product, AI-generated code needs significantly more scrutiny. The AI does not understand why a particular edge case matters to your users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Team knowledge transfer&lt;/strong&gt; — Junior developers are not just code translators. They are future senior developers. If you eliminate all junior roles, you eventually have no pipeline for senior talent. Smart companies will use vibe coding to augment juniors, not replace them entirely.&lt;/p&gt;

&lt;p&gt;The client I mentioned? He is now training his existing junior developer to use Claude Code alongside the senior dev. Instead of firing someone, he is upskilling them. The junior dev writes prompts, reviews AI output, and learns architecture patterns at 3x the normal speed because they see more code iterations per day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Debate
&lt;/h2&gt;

&lt;p&gt;The question is not "vibe coding versus traditional coding." Anyone framing it that way is selling you something.&lt;/p&gt;

&lt;p&gt;The real question is: who learns to work with AI faster?&lt;/p&gt;

&lt;p&gt;The senior developer who spent one weekend learning Claude Code now has a permanent productivity multiplier. The companies that figure out how to integrate AI coding tools into their existing teams — without gutting their junior talent pipeline — will ship faster than everyone else.&lt;/p&gt;

&lt;p&gt;AI types. It does not think. The thinking is still yours. And that is the most valuable skill you can have in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with Vibe Coding
&lt;/h2&gt;

&lt;p&gt;If you are a developer or engineering lead who wants to try this — start with one feature. Not your most critical one. Pick something from the backlog that has been sitting there for weeks. Describe it to Claude Code in plain English. See what comes back. Review it the way you would review any pull request. Ship it if it passes your standards.&lt;/p&gt;

&lt;p&gt;That is it. No complex setup. No three-month "AI transformation" initiative. One feature. One weekend. Then decide if you want to keep going.&lt;/p&gt;

&lt;p&gt;Most people who try it do.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Archit Mittal is the founder of Automate Algos. He helps businesses automate chaos using AI agents and custom workflows. Connect with him on LinkedIn &lt;a href="https://linkedin.com/in/automate-archit" rel="noopener noreferrer"&gt;@automate-archit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
      <category>automation</category>
    </item>
    <item>
      <title>How I Automated ITR Document Collection and Why Every CA Firm in India Needs This</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Mon, 30 Mar 2026 12:56:07 +0000</pubDate>
      <link>https://dev.to/automate-archit/how-i-automated-itr-document-collection-and-why-every-ca-firm-in-india-needs-this-im8</link>
      <guid>https://dev.to/automate-archit/how-i-automated-itr-document-collection-and-why-every-ca-firm-in-india-needs-this-im8</guid>
      <description>&lt;p&gt;Tax season in India is a special kind of chaos.&lt;/p&gt;

&lt;p&gt;Every year, between January and March, chartered accountants across the country enter survival mode. Their phones blow up with the same message from 200 different clients: "I'll send the documents tomorrow." Tomorrow never comes. March 31 does.&lt;/p&gt;

&lt;p&gt;My CA called me last week with the usual request — Form 16, AIS data, 12 months of bank statements. The standard package. I told him to check his email because my script had already sent everything.&lt;/p&gt;

&lt;p&gt;There was a long pause on the line.&lt;/p&gt;

&lt;p&gt;"...what script?"&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Every CA Firm Faces
&lt;/h2&gt;

&lt;p&gt;Let me paint you a picture that every chartered accountant in India will recognise.&lt;/p&gt;

&lt;p&gt;It is March 15. You have 200 clients. Each client needs to submit roughly 4-6 documents for their ITR filing. That is 800 to 1,200 individual documents that need to be collected, verified, formatted, and filed — all within about two weeks.&lt;/p&gt;

&lt;p&gt;Most of these documents live in different places. Form 16 comes from the employer's HR portal. The Annual Information Statement sits on the income tax portal. Bank statements are scattered across 2-3 different bank websites. Investment proofs are buried in email inboxes.&lt;/p&gt;

&lt;p&gt;The traditional process looks like this: the CA sends a WhatsApp message asking for documents. The client says they will send them "by evening." Three days later, the CA follows up. The client sends a blurry photo of their Form 16 taken at an angle. The CA asks for a proper PDF. Another two days pass. Multiply this by 200 clients and you understand why CAs age faster than the rest of us during tax season.&lt;/p&gt;

&lt;p&gt;The real cost is not just time. It is the cognitive load of tracking who has sent what, what is still pending, and which documents have errors that need to be re-requested. I have spoken with CA firms that hire temporary staff exclusively for document collection during January-March. One firm I worked with was spending roughly ₹80,000 per quarter on a person whose entire job was to chase clients for PDFs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built: A 40-Line Document Automation Script
&lt;/h2&gt;

&lt;p&gt;Here is what my Python script does every March, completely hands-free:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Pull Form 16 data.&lt;/strong&gt; The script logs into my employer's portal using stored credentials and downloads the Form 16 PDF. For salaried individuals, this is the single most important document and the one that gets delayed the most because people forget their HR portal passwords.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Download AIS from the Income Tax Portal.&lt;/strong&gt; The Annual Information Statement contains every financial transaction the government already knows about — salary, interest income, stock trades, mutual fund transactions. My script pulls this data programmatically and saves it as a formatted PDF.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Fetch 12 months of bank statements.&lt;/strong&gt; This was the trickiest part. Different banks have different portal structures, different session management, and different export formats. I wrote modular functions for the three banks I use — HDFC, ICICI, and SBI. Each function handles login, navigates to the statement section, sets the date range for the full financial year, and downloads the PDF.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Package and email.&lt;/strong&gt; All documents get collected into a single folder, renamed with a consistent naming convention (Form16_ArchitMittal_FY2526.pdf, AIS_ArchitMittal_FY2526.pdf, etc.), zipped, and emailed to my CA with a summary of what is included.&lt;/p&gt;

&lt;p&gt;The entire thing runs as a cron job on a ₹500/month VPS. Every March 1, it executes automatically. By the time my CA starts making his annual round of phone calls, my documents are already sitting in his inbox.&lt;/p&gt;

&lt;p&gt;Total setup time: one Sunday afternoon. Total recurring cost: ₹4 per execution in compute and API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why My CA Asked Me to Build This for His Other 200 Clients
&lt;/h2&gt;

&lt;p&gt;When I told my CA about the script, his first reaction was not "that's cool." His exact words were: "Mere baaki 200 clients ke liye bana de." (Build this for my other 200 clients.)&lt;/p&gt;

&lt;p&gt;That single sentence told me everything about the automation opportunity in Indian professional services.&lt;/p&gt;

&lt;p&gt;CA firms are not short on intelligence. These are some of the sharpest financial minds in the country. What they lack is time — specifically, time that gets consumed by mechanical, repetitive work that has nothing to do with their actual expertise.&lt;/p&gt;

&lt;p&gt;Think about what a CA's real value is: tax planning, compliance strategy, audit oversight, financial advisory. Instead, during the busiest quarter of the year, they spend 60-70% of their time on document collection and formatting. That is like hiring a surgeon and having them spend most of their day filling out admission forms.&lt;/p&gt;

&lt;p&gt;The numbers get even more interesting at scale. A mid-sized CA firm with 500 clients spends approximately 1,500-2,000 hours per year on document collection alone. At an average billing rate of ₹800/hour, that is ₹12 to ₹16 lakh in opportunity cost — revenue that could have been earned doing actual advisory work instead of chasing PDFs on WhatsApp.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture: Why Finance Automation is India's Untapped Gold Mine
&lt;/h2&gt;

&lt;p&gt;Here is what most people in the AI and automation space are missing about India.&lt;/p&gt;

&lt;p&gt;Everyone is building tools for tech companies. CRM automation for SaaS startups. Code generation for developers. Marketing copy for D2C brands. Meanwhile, the largest chunk of India's professional services sector — accounting, legal, compliance — is still running on WhatsApp messages and Excel files.&lt;/p&gt;

&lt;p&gt;India has over 3.5 lakh practising chartered accountants. Most of them serve 100-500 clients each. The vast majority use Tally for accounting, WhatsApp for client communication, and manual processes for everything in between. There is almost no automation layer connecting these tools.&lt;/p&gt;

&lt;p&gt;The opportunities are everywhere once you start looking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GST reconciliation&lt;/strong&gt; is a monthly nightmare for businesses with high transaction volumes. The GSTN portal data needs to be matched against purchase registers, and mismatches need to be identified and resolved before filing. I have seen businesses spend 3-4 days per month just on GST reconciliation — a process that a well-written script can do in under 10 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invoice processing&lt;/strong&gt; is another massive time sink. Small businesses receive invoices via email, WhatsApp, courier, and sometimes just as verbal agreements. Extracting structured data from these disparate sources and feeding it into accounting software is exactly the kind of work that AI excels at — pattern recognition across unstructured data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bank reconciliation&lt;/strong&gt; for businesses with multiple accounts across multiple banks involves downloading statements, normalising formats, matching transactions, and identifying discrepancies. Every CA firm does this manually. Every single one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Started: A Practical Roadmap
&lt;/h2&gt;

&lt;p&gt;If you are a developer looking to build in this space, or a CA firm wanting to start automating, here is what I recommend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with document collection.&lt;/strong&gt; It is the lowest-hanging fruit with the highest time savings. Build a simple script that can download Form 16, AIS, and bank statements for a single person. Then generalise it. The technical complexity is moderate — mostly web scraping, session management, and PDF handling — but the time savings are immediate and dramatic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Talk to your CA.&lt;/strong&gt; Seriously. Have a conversation about their workflow during tax season. Ask them what takes the most time. You will be surprised at how many problems are solvable with basic Python scripting and API integrations. CAs understand the value of automation intuitively because they deal with numbers all day — they just have not had someone translate their problems into code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think in workflows, not tools.&lt;/strong&gt; The mistake most automation builders make is starting with a tool and looking for problems it can solve. Instead, start with a workflow — the exact sequence of steps a human follows — and then figure out which steps can be automated. Some steps genuinely need human judgment (like deciding whether to claim a specific deduction). Others are purely mechanical (like downloading a PDF from a portal). Automate the mechanical ones ruthlessly. Leave the judgment ones to the humans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build the "pause and think" step.&lt;/strong&gt; From my own experience of over-automating my trading routine, I have learned that the best automations always include a checkpoint where a human reviews the output before it moves forward. In the case of ITR automation, this means the CA should review the collected documents before filing — never auto-file without a human in the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;March 31 is here. Somewhere right now, a CA is manually downloading their 150th Form 16 of the season while WhatsApp notifications pile up with clients asking "bhaiya, meri filing ho gayi kya?" (brother, is my filing done yet?).&lt;/p&gt;

&lt;p&gt;Next year, that CA could have a script doing the mechanical work while they focus on what actually matters — advising their clients on how to save more and comply better.&lt;/p&gt;

&lt;p&gt;The best time to automate tax season was last year. The second best time is right now.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Archit Mittal is the founder of Automate Algos. He helps businesses automate chaos using AI agents and custom workflows. Connect with him on LinkedIn &lt;a href="https://linkedin.com/in/automate-archit" rel="noopener noreferrer"&gt;@automate-archit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Automated ITR Document Preparation With 40 Lines of Python</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Fri, 27 Mar 2026 13:01:42 +0000</pubDate>
      <link>https://dev.to/automate-archit/how-i-automated-itr-document-preparation-with-40-lines-of-python-1a1j</link>
      <guid>https://dev.to/automate-archit/how-i-automated-itr-document-preparation-with-40-lines-of-python-1a1j</guid>
      <description>&lt;p&gt;My chartered accountant called me last week. Five days before March 31.&lt;/p&gt;

&lt;p&gt;His voice had that familiar panic — the same tone every CA in India carries between February and March. He needed my ITR documents. Form 16. AIS data. Twelve months of bank statements. The usual March-end package.&lt;/p&gt;

&lt;p&gt;I told him to check his email. My script had already sent everything.&lt;/p&gt;

&lt;p&gt;There was a long pause on the other end. Then: "What script?"&lt;/p&gt;

&lt;p&gt;I explained that I had written a 40-line Python script that automatically pulls my Form 16, downloads my AIS (Annual Information Statement) data, grabs twelve months of bank statements from my bank's portal, formats everything exactly the way he likes it, and emails him the complete package every March. No manual downloads. No PDF renaming. No forgetting that one statement from August.&lt;/p&gt;

&lt;p&gt;His response surprised me. He did not say "nice" or "that's cool." He said something far more interesting.&lt;/p&gt;

&lt;p&gt;"Can you build this for my other 200 clients?"&lt;/p&gt;

&lt;p&gt;That single conversation crystallised something I had been thinking about for months: the biggest automation opportunity in India is not in tech companies. It is in CA firms, brokerages, small finance teams, and the thousands of professionals who are brilliant at their core work but still copy-pasting PDFs at 2 AM during tax season.&lt;/p&gt;

&lt;h2&gt;
  
  
  The March-End Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Every year, approximately 7.5 crore (75 million) ITRs are filed in India. Behind each filing is a CA or tax professional who has to collect, verify, and organise documents from their clients. For a mid-sized CA firm handling 200-500 clients, this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sending hundreds of reminder emails and WhatsApp messages asking for documents&lt;/li&gt;
&lt;li&gt;Manually downloading Form 16 PDFs that clients forward in various formats&lt;/li&gt;
&lt;li&gt;Cross-referencing AIS data with what clients actually reported&lt;/li&gt;
&lt;li&gt;Chasing bank statements that always seem to be missing one or two months&lt;/li&gt;
&lt;li&gt;Renaming and organising files into client-specific folders&lt;/li&gt;
&lt;li&gt;Doing all of this in a 45-day window while also handling GST filings and audits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have spoken with over a dozen CA firms in the last six months. The average time spent per client just on document collection and organisation is 45-90 minutes. Multiply that by 300 clients and you get 225-450 hours — essentially two to three full-time employees doing nothing but collecting and sorting PDFs for two months.&lt;/p&gt;

&lt;p&gt;That is not accounting work. That is data entry disguised as professional service.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Script Actually Does
&lt;/h2&gt;

&lt;p&gt;Let me walk through the automation I built, because the simplicity is the entire point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Pull Form 16 Data
&lt;/h3&gt;

&lt;p&gt;Most employers now issue Form 16 through TRACES or their HRMS portals. The script connects to the relevant portal using stored credentials, downloads the latest Form 16 Part A and Part B, and saves them with a standardised naming convention. For salaried individuals with a single employer, this takes exactly one API call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Download AIS Data
&lt;/h3&gt;

&lt;p&gt;The Annual Information Statement from the Income Tax portal contains all reported financial transactions — salary, interest income, dividends, property transactions, high-value purchases. The script pulls this data and converts it into a readable summary. This is critical because AIS mismatches are the number one reason for ITR notices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Fetch Bank Statements
&lt;/h3&gt;

&lt;p&gt;This is where most people lose the most time manually. The script connects to the bank's API (or uses their download portal) to pull twelve months of statements. It handles the date ranges automatically — April to March of the relevant financial year. No more downloading month by month.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Format and Package
&lt;/h3&gt;

&lt;p&gt;Every CA has their own preferred format. Some want everything in a single PDF. Some want separate folders. Some want an Excel summary sheet on top. The script formats the output based on a configuration file — change the config once and every client's package follows the same structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Email the Package
&lt;/h3&gt;

&lt;p&gt;The final step is automatic delivery. The script compiles everything into a ZIP file and emails it to the CA with a checklist summary: which documents are included, which are missing (if any), and whether the AIS data has any discrepancies worth reviewing.&lt;/p&gt;

&lt;p&gt;Total execution time: under 3 minutes per client. Compare that to 45-90 minutes of manual work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Numbers: Why This Matters for CA Firms
&lt;/h2&gt;

&lt;p&gt;Let me lay out the business case with real numbers, because abstract automation talk means nothing without specifics.&lt;/p&gt;

&lt;p&gt;A CA firm with 300 clients spends roughly 300 hours on document collection at the conservative end. If a junior accountant costs the firm around ₹25,000 per month (₹150 per hour), that is ₹45,000 worth of labour — every tax season — on a task that adds zero intellectual value.&lt;/p&gt;

&lt;p&gt;But the real cost is not the labour. It is the opportunity cost. Those 300 hours are 300 hours that could go towards actual tax planning, advisory work, and the kind of high-value services that clients pay a premium for. A CA billing ₹2,000 per hour for advisory work who spends 100 of those hours on document collection is leaving ₹2,00,000 on the table.&lt;/p&gt;

&lt;p&gt;The script I built cost me a Sunday afternoon. The version I am building for CA firms — with a proper dashboard, client management, and multi-bank support — will save the average firm ₹3-5 lakh per tax season in recovered billable hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why CA Firms Are the Perfect Automation Clients
&lt;/h2&gt;

&lt;p&gt;After working with traders, e-commerce businesses, and SaaS companies on automation, I have come to believe that CA firms are the single most underserved market for business automation in India. Here is why:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Their work is highly repetitive.&lt;/strong&gt; Tax filing follows the same steps for every client, every year. The inputs change but the process does not. This is automation's sweet spot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They already have the data.&lt;/strong&gt; Unlike businesses that need to first digitise their operations, CA firms already work with structured digital data — PDFs, Excel sheets, portal downloads. The data is there. It just needs to flow automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pain is seasonal but intense.&lt;/strong&gt; March-end is not just busy. It is existentially stressful for most CA firms. Automation that alleviates even 30% of that March crunch is worth significant money to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They serve as multipliers.&lt;/strong&gt; One CA firm automates their workflow and suddenly 300 of their clients benefit from faster, more accurate filings. The ripple effect is massive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond ITR: The Full CA Automation Stack
&lt;/h2&gt;

&lt;p&gt;Document collection is just the starting point. Once a CA firm sees what automation can do for their ITR workflow, the next questions come naturally:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GST Reconciliation:&lt;/strong&gt; Matching GSTR-2A with purchase registers is a monthly headache for every firm. A Python script that pulls both datasets and highlights mismatches saves 10-15 hours per month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TDS Certificate Collection:&lt;/strong&gt; Chasing Form 16A from multiple deductors for a single client is one of the most frustrating parts of tax practice. Automated tracking and download changes the game.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client Communication:&lt;/strong&gt; Automated WhatsApp reminders that go out when documents are missing — not generic blasts, but personalised messages that say "We have your Form 16 but are still waiting for your HDFC Bank statement for October."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audit Trail Generation:&lt;/strong&gt; Every download, every email, every document version — automatically logged. When the tax department asks questions two years later, the firm has a complete digital paper trail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started: What You Actually Need
&lt;/h2&gt;

&lt;p&gt;You do not need to be a Python developer to begin automating your CA practice. Here is the minimum viable automation stack:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python 3.x&lt;/strong&gt; with the requests library for API calls, pandas for data formatting, and smtplib for email automation. That is it. No frameworks, no cloud infrastructure, no monthly subscription.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API access&lt;/strong&gt; to your clients' financial portals. This is the tricky part — it requires client consent and proper credential management. Start with your own accounts to prove the concept.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A configuration file&lt;/strong&gt; (even a simple Excel sheet works) that maps each client to their data sources, CA's preferred format, and delivery email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;30 minutes of setup per client&lt;/strong&gt; the first time. After that, every subsequent tax season is fully automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;Somewhere in India right now, a CA is manually downloading their 200th Form 16 of the season. They are brilliant at tax law. They could be advising clients on Section 80C optimisation or structuring HUF transactions. Instead, they are renaming PDFs.&lt;/p&gt;

&lt;p&gt;This is not a technology problem. Python has been able to do this for over a decade. It is an awareness problem. Most CA firms do not know that a Sunday afternoon of scripting can reclaim hundreds of hours of their practice every year.&lt;/p&gt;

&lt;p&gt;The finance automation market in India is not a future opportunity. It is a present one. The tools exist. The data is digital. The pain is real and recurring. The only missing piece is someone to connect the dots.&lt;/p&gt;

&lt;p&gt;That is exactly what I do. I automate chaos.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Archit Mittal is the founder of Automate Algos. He helps businesses automate chaos using AI agents and custom workflows. Connect with him on LinkedIn &lt;a href="https://linkedin.com/in/automate-archit" rel="noopener noreferrer"&gt;@automate-archit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Indian Small Businesses Lose ₹50,000 Every Month to Tasks That Could Be Automated for Free</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Wed, 25 Mar 2026 13:03:05 +0000</pubDate>
      <link>https://dev.to/automate-archit/indian-small-businesses-lose-50000-every-month-to-tasks-that-could-be-automated-for-free-4ah4</link>
      <guid>https://dev.to/automate-archit/indian-small-businesses-lose-50000-every-month-to-tasks-that-could-be-automated-for-free-4ah4</guid>
      <description>&lt;p&gt;I surveyed 20 small business founders in India last week. One simple question: &lt;em&gt;"What's your biggest time drain?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answers were painfully consistent. Customer replies, invoice follow-ups, data entry, social media scheduling — the same handful of tasks eating away 20+ hours every week across nearly every business I spoke to.&lt;/p&gt;

&lt;p&gt;When I did the math, the number was staggering. Most of these businesses were bleeding ₹50,000 or more per month — not on software, not on infrastructure — on &lt;em&gt;human hours wasted on work that machines should be doing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the thing: almost none of them knew that these tasks could be fully automated, often for ₹0.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Cost of "Just Doing It Manually"
&lt;/h2&gt;

&lt;p&gt;Small business owners in India are some of the hardest-working people I know. But hard work and smart work are two very different things. When you're manually replying to the same 15 customer queries every day, chasing invoices over WhatsApp, or copy-pasting order details from forms into spreadsheets, you're not building your business — you're running on a hamster wheel.&lt;/p&gt;

&lt;p&gt;Here's what the 20 founders I surveyed reported as their weekly time drains:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customer replies:&lt;/strong&gt; 8 hours per week. Most of these are the same 10-15 questions asked repeatedly — order status, pricing, return policy, payment confirmation. A well-configured auto-reply system or AI chatbot handles 80% of these without human intervention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invoice follow-ups:&lt;/strong&gt; 6 hours per week. Sending reminders, checking payment status, escalating overdue invoices. This is a classic automation candidate — trigger on due date, auto-send a WhatsApp or email reminder, escalate if unpaid after a set number of days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data entry from forms:&lt;/strong&gt; 5 hours per week. Google Form responses, website enquiries, order details — all being manually entered into spreadsheets or accounting software. A simple script or webhook can move this data automatically the moment it arrives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Social media scheduling:&lt;/strong&gt; 4 hours per week. Writing captions, finding the right time to post, switching between platforms. Batch your content creation once a week, schedule it with a cron job or a free scheduler, and reclaim those hours.&lt;/p&gt;

&lt;p&gt;That's &lt;strong&gt;23 hours per week&lt;/strong&gt; — nearly three full working days — gone to tasks that a well-designed automation handles in the background with zero ongoing effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Problem Is Worse in India
&lt;/h2&gt;

&lt;p&gt;India has a unique automation gap. Large enterprises have dedicated IT teams building internal tools and integrations. But the 63 million+ MSMEs that form the backbone of the Indian economy? Most of them are running on WhatsApp, Tally, Excel, and sheer willpower.&lt;/p&gt;

&lt;p&gt;The reasons I've seen after working with hundreds of these businesses:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Automation is expensive"&lt;/strong&gt; — This was the most common misconception. Business owners assume automation means buying expensive enterprise software. In reality, a Python script running on a ₹500/month server can replace tasks that cost ₹50,000/month in manual labor. Google Apps Script is completely free and can automate most spreadsheet and email workflows. Even WhatsApp Business API has free tiers for small volumes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I don't have a tech team"&lt;/strong&gt; — You don't need one. Modern AI coding tools like Claude Code and Cursor can generate working automation scripts from plain English descriptions. I've seen business owners with zero coding background set up invoice automation in an afternoon using AI-assisted coding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"My processes are too unique"&lt;/strong&gt; — Every business owner thinks their workflow is special. In my experience, 90% of small business operations fall into the same five categories: lead capture, customer communication, invoicing, data management, and reporting. The specifics vary, but the automation patterns are nearly identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Five Automations Every Indian Small Business Should Build First
&lt;/h2&gt;

&lt;p&gt;Based on the patterns I've seen across 10,000+ businesses and traders I've worked with, here are the five highest-ROI automations to start with:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Customer Query Auto-Response
&lt;/h3&gt;

&lt;p&gt;Set up a WhatsApp Business API integration that reads incoming messages, matches them against your FAQ database, and sends instant replies. For the 20% of queries that need human judgment, route them to your phone with a priority tag. Time saved: &lt;strong&gt;6-8 hours/week&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The setup is straightforward — a small script that listens for incoming messages via webhook, matches keywords against a JSON file of common questions and answers, and sends the response back via the API. Total cost: ₹0 if you're under the free message limit.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Invoice Follow-Up Pipeline
&lt;/h3&gt;

&lt;p&gt;Connect your billing system (Tally, Zoho, or even a Google Sheet) to an automated reminder workflow. When an invoice crosses its due date, the system sends a polite WhatsApp reminder. Three days later, a firmer email. Seven days later, it flags the case for your personal attention. Time saved: &lt;strong&gt;5-6 hours/week&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The key insight here is that 70% of late payments aren't intentional — people just forget. An automated nudge at the right time recovers most of them without any awkward phone calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Form-to-System Data Bridge
&lt;/h3&gt;

&lt;p&gt;Whether you're collecting leads from a website form, orders from Google Forms, or enquiries from Instagram DMs, the data should flow automatically into your master spreadsheet or CRM. A webhook endpoint that receives form data and appends it to Google Sheets takes about 20 lines of code. Time saved: &lt;strong&gt;4-5 hours/week&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Daily Business Summary Report
&lt;/h3&gt;

&lt;p&gt;Instead of manually checking five different dashboards every morning, build a script that pulls yesterday's sales from your POS, pending orders from your sheet, overdue invoices from Tally, and sends you a single WhatsApp message at 8 AM with everything you need to know. Time saved: &lt;strong&gt;3-4 hours/week&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the automation that business owners love the most — not because it saves the most time, but because it gives them clarity. You start your day knowing exactly what needs attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Bank Statement Categorization
&lt;/h3&gt;

&lt;p&gt;If you're still manually categorizing bank transactions for your CA at month-end, stop. A Python script using basic pattern matching (or an AI model for tricky ones) can read your bank statement CSV, categorize each transaction (salary, rent, vendor payment, GST, etc.), and spit out a clean report ready for your accountant. Time saved: &lt;strong&gt;8-10 hours/month&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ₹0 Automation Stack
&lt;/h2&gt;

&lt;p&gt;You don't need expensive SaaS subscriptions to automate a small business. Here's the stack I recommend to founders who want to start without spending anything:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google Apps Script&lt;/strong&gt; — Free, runs on Google's servers, integrates natively with Sheets, Gmail, Calendar, and Forms. Perfect for email automation, data processing, and scheduled reports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python + cron on a ₹500 VPS&lt;/strong&gt; — For anything that needs to run on a schedule — data pulls, report generation, API integrations. A basic DigitalOcean or AWS Lightsail instance handles most small business automation needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp Business API&lt;/strong&gt; — Free tier covers up to 1,000 conversations per month. More than enough for most small businesses to automate customer replies and send notifications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI coding assistants&lt;/strong&gt; — Claude Code, Cursor, or GitHub Copilot can write the actual automation scripts for you. Describe what you want in plain English, get working code, deploy it. The barrier to entry has never been lower.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google Sheets as a database&lt;/strong&gt; — For businesses that aren't ready for a proper database, Sheets works surprisingly well as a lightweight backend. Combined with Apps Script, it becomes a free automation platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real ROI: It's Not Just About Money
&lt;/h2&gt;

&lt;p&gt;Yes, automating 23 hours of weekly manual work saves roughly ₹50,000/month in labor costs. But the real return is something bigger — it frees the founder to actually &lt;em&gt;run&lt;/em&gt; the business.&lt;/p&gt;

&lt;p&gt;Every hour a small business owner spends copying data from one spreadsheet to another is an hour they're not spending on product development, customer relationships, or strategic planning. The businesses that grow are the ones where the founder's time is spent on decisions, not data entry.&lt;/p&gt;

&lt;p&gt;As my father always says — &lt;em&gt;"Time waste karna sabse bada loss hai."&lt;/em&gt; He's not wrong. And in 2026, wasting time on tasks a script can handle is not just inefficient — it's a competitive disadvantage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start With One Thing
&lt;/h2&gt;

&lt;p&gt;If you've read this far and you're thinking "this sounds great but where do I even begin?" — here's my advice: pick one task. Just one. The most repetitive, soul-crushing task in your week.&lt;/p&gt;

&lt;p&gt;Then automate it. Use AI to write the script if you need to. Deploy it on a free server. Watch it run.&lt;/p&gt;

&lt;p&gt;Once you see that first automation working — handling something that used to eat hours of your week — you'll never look at manual work the same way again.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Archit Mittal is the founder of Automate Algos. He helps businesses automate chaos using AI agents and custom workflows. Connect with him on LinkedIn &lt;a href="https://linkedin.com/in/automate-archit" rel="noopener noreferrer"&gt;@automate-archit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>ai</category>
      <category>productivity</category>
      <category>startup</category>
    </item>
    <item>
      <title>How I Saved My Mom's Small Business ₹1,50,000/Year With a Free WhatsApp Automation</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Mon, 23 Mar 2026 12:56:01 +0000</pubDate>
      <link>https://dev.to/automate-archit/how-i-saved-my-moms-small-business-150000year-with-a-free-whatsapp-automation-2c3b</link>
      <guid>https://dev.to/automate-archit/how-i-saved-my-moms-small-business-150000year-with-a-free-whatsapp-automation-2c3b</guid>
      <description>&lt;p&gt;&lt;strong&gt;₹1,50,000 per year.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's what my mom's small business was spending on a person whose only job was to read WhatsApp orders and type them into a register. Not a spreadsheet. A physical register.&lt;/p&gt;

&lt;p&gt;Last month, I sat with her for 2 hours and built a simple workflow that completely eliminated that cost. The tools were free. The setup was straightforward. And the next morning, she called me and said — &lt;em&gt;"Beta, vo 3 ghante mein karta tha, ye 3 second mein ho gaya."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Translation: "Son, what used to take 3 hours now happens in 3 seconds."&lt;/p&gt;

&lt;p&gt;This isn't a story about replacing people. She used the money she saved to hire a delivery person instead. More jobs, not fewer. That's the real automation story nobody talks about.&lt;/p&gt;

&lt;p&gt;Here's exactly how I built it — and how you can do the same for any small business in India.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Manual WhatsApp Order Entry
&lt;/h2&gt;

&lt;p&gt;If you run or know a small business in India, you've seen this workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Customer sends a WhatsApp message — "2 kg atta, 1 kg sugar, deliver by 5 PM"&lt;/li&gt;
&lt;li&gt;Someone reads the message, opens a register or Excel sheet, and types it in manually&lt;/li&gt;
&lt;li&gt;They send a reply — "Order received, will deliver by 5 PM"&lt;/li&gt;
&lt;li&gt;They repeat this 30-50 times a day&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My mom's business was getting 40+ orders daily on WhatsApp. The person handling this spent 3 hours every morning just entering data and replying. Three hours of repetitive, error-prone work that a machine can do in seconds.&lt;/p&gt;

&lt;p&gt;The cost breakdown was painful: ₹12,500/month salary for a task that added zero creative or strategic value to the business. Over a year, that's ₹1,50,000 spent on copy-pasting messages into a sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: n8n + WhatsApp + Google Sheets
&lt;/h2&gt;

&lt;p&gt;I used &lt;strong&gt;n8n&lt;/strong&gt; — an open-source workflow automation tool — to build the entire pipeline. Here's what the workflow does:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Capture incoming WhatsApp messages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using the WhatsApp Business API (via a provider like Twilio or the official WhatsApp Cloud API), every incoming message triggers an n8n webhook. The message content, sender's phone number, and timestamp all flow into the workflow automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Parse the order&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where it gets clever. I connected a simple AI node (Claude API) that reads the raw WhatsApp message and extracts structured data — item names, quantities, delivery time, and any special instructions. Indian customers write orders in a mix of Hindi, English, and Hinglish. The AI handles all three without breaking a sweat.&lt;/p&gt;

&lt;p&gt;A message like "bhai 2 packet atta aur 1 kg cheeni, 6 baje tak bhej dena" gets parsed into:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Quantity&lt;/th&gt;
&lt;th&gt;Delivery Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Atta&lt;/td&gt;
&lt;td&gt;2 packets&lt;/td&gt;
&lt;td&gt;6:00 PM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sugar&lt;/td&gt;
&lt;td&gt;1 kg&lt;/td&gt;
&lt;td&gt;6:00 PM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Write to Google Sheets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The parsed order data gets written into a Google Sheet — one row per order with columns for customer name, phone number, items, quantities, delivery time, and status. This sheet becomes the live order dashboard that anyone in the business can access from their phone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Send an automated confirmation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The workflow sends a WhatsApp reply back to the customer confirming their order with the exact items and delivery time. If the AI couldn't parse something (maybe a new product name it hasn't seen), it flags the message for a human to review instead of guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why n8n and Not Zapier?
&lt;/h2&gt;

&lt;p&gt;This is a question I get constantly. The answer comes down to cost and control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zapier&lt;/strong&gt; would cost ₹12,000-15,000/year for this kind of workflow with 40+ daily triggers. For a small business that was already spending ₹1,50,000/year on manual labor, adding another subscription defeats part of the purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;n8n&lt;/strong&gt; is open-source. You can self-host it on a ₹500/month VPS (or even a Raspberry Pi sitting in your shop) and run unlimited workflows with unlimited triggers. The total infrastructure cost for my mom's setup is roughly ₹6,000/year — a 96% reduction from the original ₹1,50,000.&lt;/p&gt;

&lt;p&gt;Here's the honest comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Zapier&lt;/th&gt;
&lt;th&gt;n8n&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly cost (this workflow)&lt;/td&gt;
&lt;td&gt;₹1,000-1,250&lt;/td&gt;
&lt;td&gt;₹500 (VPS hosting)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Annual cost&lt;/td&gt;
&lt;td&gt;₹12,000-15,000&lt;/td&gt;
&lt;td&gt;₹6,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trigger limits&lt;/td&gt;
&lt;td&gt;Capped by plan&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hosting&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WhatsApp integration&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Full API access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Slightly higher&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For someone who's never built a workflow before, Zapier is easier to start with. But for any Indian small business watching costs carefully, n8n pays for itself in the first month.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results After 30 Days
&lt;/h2&gt;

&lt;p&gt;The numbers speak louder than any pitch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time saved:&lt;/strong&gt; 3 hours/day → approximately 90 hours/month of manual work eliminated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error rate:&lt;/strong&gt; Down from roughly 8-10 wrong entries/week to near zero&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response time:&lt;/strong&gt; Customer replies went from 15-30 minutes (human delay) to under 5 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; ₹12,500/month → ₹500/month (96% reduction)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Side benefit:&lt;/strong&gt; The Google Sheet doubles as an inventory tracker — she can now see which products sell most and when&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the number that matters most? My mom used the ₹1,50,000/year she saved to hire a delivery person. The business grew because automation freed up capital that went right back into operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Build This for Your Business (Quick Start)
&lt;/h2&gt;

&lt;p&gt;If you want to replicate this, here's the simplified roadmap:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1 — Set up infrastructure (1 hour)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Get a VPS from any Indian cloud provider (DigitalOcean, Hetzner, or even AWS Lightsail). Install n8n using their Docker setup — it takes about 15 minutes if you follow their documentation. Set up WhatsApp Business API access through Meta's Cloud API (free tier covers up to 1,000 conversations/month).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2 — Build the workflow (1 hour)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a webhook trigger in n8n. Connect it to your WhatsApp Business API. Add an AI node for message parsing (Claude API's Haiku model costs fractions of a paisa per message). Connect Google Sheets as the output destination. Add a WhatsApp reply node for confirmations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3 — Test and go live (30 minutes)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Send test messages from different phone numbers. Check that orders parse correctly in Hindi, English, and Hinglish. Verify Google Sheet entries are accurate. Set up an error notification (Telegram bot works great for this) so you know immediately if something fails.&lt;/p&gt;

&lt;p&gt;The total setup time is about 2-3 hours for someone comfortable with basic tech tools. If you're a developer, you can probably do it in under an hour.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture: Why This Matters for Indian Small Businesses
&lt;/h2&gt;

&lt;p&gt;India has over 63 million MSMEs. Most of them run critical business operations through WhatsApp. And most of them are paying real money — or spending valuable hours — on tasks that automation handles for free.&lt;/p&gt;

&lt;p&gt;The tools exist today. n8n is free. The WhatsApp API has a generous free tier. Google Sheets is free. The only investment is a few hours of setup time.&lt;/p&gt;

&lt;p&gt;The businesses that figure this out in 2026 will have a massive cost advantage over those that don't. And it's not about cutting jobs — it's about redirecting human effort from data entry to work that actually grows the business.&lt;/p&gt;

&lt;p&gt;My mom's delivery person is busier than ever. Her customers get faster replies. Her order accuracy went up. Everyone won.&lt;/p&gt;

&lt;p&gt;That's what good automation looks like.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Archit Mittal is the founder of Automate Algos. He helps businesses automate chaos using AI agents, n8n, and custom workflows. Connect with him on LinkedIn &lt;a href="https://linkedin.com/in/automate-archit" rel="noopener noreferrer"&gt;@automate-archit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Saved a Client ₹85,000/Month on AI API Costs — A Practical Breakdown</title>
      <dc:creator>Archit Mittal</dc:creator>
      <pubDate>Sat, 21 Mar 2026 12:48:11 +0000</pubDate>
      <link>https://dev.to/automate-archit/how-i-saved-a-client-85000month-on-ai-api-costs-a-practical-breakdown-lh1</link>
      <guid>https://dev.to/automate-archit/how-i-saved-a-client-85000month-on-ai-api-costs-a-practical-breakdown-lh1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally shared on LinkedIn where it reached 10,000+ professionals. Expanded with technical details and actionable strategies here.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;A client was burning through &lt;strong&gt;₹85,000/month&lt;/strong&gt; on AI API calls.&lt;/p&gt;

&lt;p&gt;In one weekend, we cut that to under &lt;strong&gt;₹12,000&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'm not exaggerating. This is real money saved by a real company that had no idea they were hemorrhaging cash on AI infrastructure.&lt;/p&gt;

&lt;p&gt;The worst part? They thought that's just "the cost of doing AI."&lt;/p&gt;

&lt;p&gt;It's not.&lt;/p&gt;

&lt;p&gt;If you're using AI APIs in production—Claude, GPT-4, Gemini, whatever—there's a 90% chance you're overspending. This article walks through exactly how we diagnosed the problem and fixed it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Why Your AI Bill Looks Like a Mortgage Payment
&lt;/h2&gt;

&lt;p&gt;Before the audit, here's what the client was doing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using Opus (₹15/1M tokens) for simple tasks&lt;/strong&gt; — asking GPT-4-level models to classify customer emails or generate summaries when Haiku (₹0.80/1M tokens) would work fine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero prompt caching&lt;/strong&gt; — sending the same 50KB system prompt with every API call. That's like shipping the same dictionary with every word lookup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No batch processing&lt;/strong&gt; — every request hit the API in real-time, even for non-urgent tasks that could run at night at 50% discount.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redundant API calls&lt;/strong&gt; — the app was calling the same input multiple times within seconds because there was no response caching. Imagine asking your friend the same question twice instead of remembering the answer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Token bloat&lt;/strong&gt; — prompts weren't optimized. XML wrappers, unnecessary examples, verbose instructions—all padding the token count.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The client thought they needed the "best" model for everything. In reality, they needed the &lt;strong&gt;right&lt;/strong&gt; model for each task.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Audit: Finding Your Money Leaks
&lt;/h2&gt;

&lt;p&gt;Here's the systematic process we followed:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Collect Everything
&lt;/h3&gt;

&lt;p&gt;We enabled detailed logging on all API calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_api_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt_hash&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;log_entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;input_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;output_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# To detect redundancy
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total_cost_usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calculate_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api_audit.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One week of logs told us everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Categorize by Task Type
&lt;/h3&gt;

&lt;p&gt;We grouped calls into buckets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Email classification&lt;/strong&gt; — 12,000 calls/week (should be Haiku)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content summarization&lt;/strong&gt; — 4,000 calls/week (Sonnet max)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex research synthesis&lt;/strong&gt; — 800 calls/week (Opus only)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code review&lt;/strong&gt; — 2,200 calls/week (Sonnet)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt engineering iteration&lt;/strong&gt; — 3,500 calls/week (varies, but lots of waste here)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Match Model to Complexity
&lt;/h3&gt;

&lt;p&gt;This is where the magic happens. Not every task needs Opus.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task Type&lt;/th&gt;
&lt;th&gt;Current Model&lt;/th&gt;
&lt;th&gt;Optimal Model&lt;/th&gt;
&lt;th&gt;Annual Savings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Email classification&lt;/td&gt;
&lt;td&gt;GPT-4 (₹12/1M in)&lt;/td&gt;
&lt;td&gt;Claude Haiku (₹0.80/1M in)&lt;/td&gt;
&lt;td&gt;₹42,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customer support QA&lt;/td&gt;
&lt;td&gt;GPT-4&lt;/td&gt;
&lt;td&gt;Claude Sonnet (₹3/1M in)&lt;/td&gt;
&lt;td&gt;₹28,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content summarization&lt;/td&gt;
&lt;td&gt;GPT-4 Turbo&lt;/td&gt;
&lt;td&gt;Claude Sonnet&lt;/td&gt;
&lt;td&gt;₹18,500+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex research&lt;/td&gt;
&lt;td&gt;GPT-4 Turbo&lt;/td&gt;
&lt;td&gt;Claude Opus (₹15/1M)&lt;/td&gt;
&lt;td&gt;₹0 (necessary)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt iteration&lt;/td&gt;
&lt;td&gt;GPT-4&lt;/td&gt;
&lt;td&gt;Claude Haiku&lt;/td&gt;
&lt;td&gt;₹8,500+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 4: Spot Redundancy
&lt;/h3&gt;

&lt;p&gt;Using the prompt hash from our logs, we found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Same system prompt sent 47,000 times in one month.&lt;/strong&gt; With caching, that would be 1 full call + 46,999 cached calls (90% cost reduction on those).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same user inputs processed multiple times&lt;/strong&gt; (within seconds). A simple &lt;code&gt;(input_hash, output)&lt;/code&gt; cache would eliminate this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry loops without exponential backoff&lt;/strong&gt; — failed calls being retried immediately, multiplying tokens wasted.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The 5 Optimization Strategies That Worked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Model Matching (30% savings)
&lt;/h3&gt;

&lt;p&gt;Simple rule: &lt;strong&gt;Use the simplest model that works for your task.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Haiku&lt;/strong&gt; — Classification, formatting, extraction, simple routing (₹0.80/1M input)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sonnet&lt;/strong&gt; — Summarization, content generation, moderate reasoning (₹3/1M input)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opus&lt;/strong&gt; — Complex multi-step reasoning, code review, research synthesis (₹15/1M input)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We moved 60% of the client's workload from Opus/GPT-4 to Haiku/Sonnet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing:&lt;/strong&gt; Don't guess. Run a small batch through each model and measure latency + quality. Usually Haiku is 10% quality loss with 50-90% cost reduction.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Prompt Caching (45% savings)
&lt;/h3&gt;

&lt;p&gt;Claude's prompt caching feature is a game-changer. It works like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First call: Send full prompt. (₹15 per 1M tokens)&lt;/li&gt;
&lt;li&gt;Next 5,000+ calls: Send only the new query part. (₹0.30 per 1M tokens cached)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Static system prompt (gets cached)
&lt;/span&gt;&lt;span class="n"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a customer support email classifier.
Categories: billing, technical_support, feature_request, complaint, other.
Respond with JSON: {&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 0.95, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="c1"&gt;# This prompt gets cached on first call
&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-opus-4-1-20250805&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ephemeral&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# Cache for 5 mins
&lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Can you help me reset my password?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# usage.cache_read_input_tokens &amp;gt; 0 means caching worked
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the client:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50KB system prompt × 30,000 calls/month = 1.5B cached tokens&lt;/li&gt;
&lt;li&gt;Cached tokens cost 90% less&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monthly savings: ₹18,000+&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Batch Processing (35% savings for async tasks)
&lt;/h3&gt;

&lt;p&gt;Not every API call needs to be real-time. Use the batch API for non-urgent tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normal API: ₹15/1M input tokens&lt;/li&gt;
&lt;li&gt;Batch API: ₹5/1M input tokens (50% discount, 24-hour turnaround)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Prepare batch requests
&lt;/span&gt;&lt;span class="n"&gt;batch_requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;emails_to_classify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# 10,000 emails
&lt;/span&gt;    &lt;span class="n"&gt;batch_requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;custom_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;params&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-opus-4-1-20250805&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Classify this email...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# Submit batch
&lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;batch_requests&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Batch ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Process results later
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the client's non-urgent tasks (nightly reports, weekly summaries), batch processing cut costs in half.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Response Caching (20% savings)
&lt;/h3&gt;

&lt;p&gt;Don't ask the API the same question twice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;

&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;  &lt;span class="c1"&gt;# Or Redis for production
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Create deterministic hash of input
&lt;/span&gt;    &lt;span class="n"&gt;input_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Check cache first
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;input_hash&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;input_hash&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Call API
&lt;/span&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;input_hash&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

&lt;span class="c1"&gt;# First call: hits API
&lt;/span&gt;&lt;span class="n"&gt;result1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Can I reset my password?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-haiku-4-5-20251001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Second call: same input, returns from cache instantly
&lt;/span&gt;&lt;span class="n"&gt;result2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Can I reset my password?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-haiku-4-5-20251001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client had duplicate email classifications happening within seconds. A simple cache (Redis, in-memory, doesn't matter) eliminated ~20% of redundant calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Token Optimization (15% savings)
&lt;/h3&gt;

&lt;p&gt;Smaller prompts = fewer tokens = lower costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an expert customer service AI. Your role is to take customer emails
and classify them into one of several predefined categories. Please read
the email carefully and determine which category it belongs to.

Categories:
1. Billing &amp;amp; Payments
2. Technical Support &amp;amp; Bugs
3. Feature Requests
4. Complaints &amp;amp; Escalations
5. Other

Please respond in JSON format with the following structure:
{
  "category": "...",
  "confidence": 0.0-1.0,
  "reasoning": "..."
}

Here's the email:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Classify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;email&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;into:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;billing,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;technical,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;feature_request,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;complaint,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;other.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Response:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0-1.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;Email:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same output, 60% fewer tokens.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Results: Before &amp;amp; After
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;th&gt;Savings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly API cost&lt;/td&gt;
&lt;td&gt;₹85,000&lt;/td&gt;
&lt;td&gt;₹12,000&lt;/td&gt;
&lt;td&gt;₹73,000 (86%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg tokens/call&lt;/td&gt;
&lt;td&gt;2,840&lt;/td&gt;
&lt;td&gt;1,100&lt;/td&gt;
&lt;td&gt;61% reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API calls/month&lt;/td&gt;
&lt;td&gt;340,000&lt;/td&gt;
&lt;td&gt;280,000&lt;/td&gt;
&lt;td&gt;18% fewer (redundancy removed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency (p95)&lt;/td&gt;
&lt;td&gt;850ms&lt;/td&gt;
&lt;td&gt;420ms&lt;/td&gt;
&lt;td&gt;50% faster (cache hits)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System accuracy&lt;/td&gt;
&lt;td&gt;94%&lt;/td&gt;
&lt;td&gt;94.2%&lt;/td&gt;
&lt;td&gt;+0.2% (better model matching)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The real win? The client now has better performance AND lower costs. That's the point of optimization—efficiency compounds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your Turn: The Checklist
&lt;/h2&gt;

&lt;p&gt;You can apply this to any AI project. Here's what to do Monday morning:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 1: Audit&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Enable detailed logging on all API calls (model, tokens, task type)&lt;/li&gt;
&lt;li&gt;[ ] Collect one week of data&lt;/li&gt;
&lt;li&gt;[ ] Categorize tasks by complexity&lt;/li&gt;
&lt;li&gt;[ ] Calculate per-task costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Week 2: Low-Hanging Fruit&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Downgrade non-critical tasks to cheaper models (test quality first)&lt;/li&gt;
&lt;li&gt;[ ] Implement response caching if you have duplicate inputs&lt;/li&gt;
&lt;li&gt;[ ] Optimize prompts (remove verbosity, examples, unnecessary structure)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Week 3: Advanced Optimizations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Set up prompt caching for static system prompts&lt;/li&gt;
&lt;li&gt;[ ] Migrate non-urgent tasks to batch processing&lt;/li&gt;
&lt;li&gt;[ ] Implement request deduplication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Week 4: Measure &amp;amp; Iterate&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Compare new costs vs. baseline&lt;/li&gt;
&lt;li&gt;[ ] Monitor quality metrics (didn't go down, right?)&lt;/li&gt;
&lt;li&gt;[ ] Automate the audit—keep logging, check monthly&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tools &amp;amp; Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Logging &amp;amp; Analytics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python &lt;code&gt;anthropic&lt;/code&gt; SDK (built-in logging)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;langsmith.com&lt;/code&gt; (trace API calls across frameworks)&lt;/li&gt;
&lt;li&gt;Datadog / Elastic (if you're at scale)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Caching:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis (production caching)&lt;/li&gt;
&lt;li&gt;LRU cache (Python &lt;code&gt;functools.lru_cache&lt;/code&gt; for testing)&lt;/li&gt;
&lt;li&gt;Claude prompt caching (native, no setup)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Batch Processing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Batch API (native)&lt;/li&gt;
&lt;li&gt;OpenAI Batch API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cost Calculators:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;anthropic.com/pricing&lt;/code&gt; (Claude)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openai.com/pricing&lt;/code&gt; (GPT models)&lt;/li&gt;
&lt;li&gt;Build your own: &lt;code&gt;(input_tokens × input_rate + output_tokens × output_rate) / 1_000_000&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;This client went from thinking "AI is expensive" to building a sustainable, cost-efficient AI infrastructure.&lt;/p&gt;

&lt;p&gt;The same principles apply whether you're:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building an AI chatbot for customer support&lt;/li&gt;
&lt;li&gt;Automating content generation at scale&lt;/li&gt;
&lt;li&gt;Running batch analysis on document collections&lt;/li&gt;
&lt;li&gt;Fine-tuning models for specific tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; Every ₹ you save on API costs is ₹1 that can go toward better infrastructure, faster iteration, or hiring more engineers.&lt;/p&gt;

&lt;p&gt;Start with the audit. Everything else follows.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;I'm Archit Mittal (&lt;a class="mentioned-user" href="https://dev.to/automate-archit"&gt;@automate-archit&lt;/a&gt;), an automation engineer helping companies save money and time with AI workflows. I've helped 30+ clients optimize their AI spending, cut API costs by 50-85%, and build sustainable automation architectures.&lt;/p&gt;

&lt;p&gt;If you're dealing with similar cost problems or want to discuss AI automation strategies, let's connect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://linkedin.com/in/automate-archit" rel="noopener noreferrer"&gt;@automate-archit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Twitter/X:&lt;/strong&gt; &lt;a href="https://twitter.com/automate_archit" rel="noopener noreferrer"&gt;@automate_archit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1-on-1 consultations:&lt;/strong&gt; &lt;a href="https://topmate.io/automate_archit" rel="noopener noreferrer"&gt;topmate.io/automate_archit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to share this with your team. Cost optimization benefits everyone.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
