How I Automated a Full SaaS Workflow with Nothing but Python Scripts
From user onboarding and database updates to email reports and billing — here’s how I replaced entire microservices using Python, a few APIs, and a lot of sleep deprivation.
1. Why I Ditched No-Code and Wrote Python Instead Let me be blunt: no-code tools are great until you hit a wall. For my SaaS side project, I started with Zapier, Airtable, and Retool. But things got messy. Workflows failed silently. Debugging was a nightmare. I needed precision. That’s when I replaced every moving part with Python. Now, my SaaS operations run through a single repo:
- Signup → database insert
- User welcome → transactional email
- Daily analytics → Slack report
- Stripe billing → webhook processor
- Admin dashboard → Streamlit app
Everything. In. Python. Let’s break it down.
2. User Signup → PostgreSQL Insert + Welcome Email As soon as a user signs up through the frontend, a webhook hits my Flask endpoint. Here’s how I handle it:
from flask import Flask, request
import psycopg2
import smtplib
from email.mime.text import MIMEText
app = Flask(__name__)
@app.route('/signup', methods=['POST'])
def signup():
data = request.json
email = data['email']
name = data['name']
# Insert into PostgreSQL
conn = psycopg2.connect("dbname=saas user=admin password=secret")
cur = conn.cursor()
cur.execute("INSERT INTO users (name, email) VALUES (%s, %s)", (name, email))
conn.commit()
conn.close()
# Send welcome email
msg = MIMEText(f"Welcome {name}! You're in 🚀")
msg['Subject'] = "Welcome to Our App"
msg['From'] = "[email protected]"
msg['To'] = email
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login("[email protected]", "password")
server.send_message(msg)
return {"status": "ok"}
No third-party automation platform. Just a clean webhook, raw SQL, and SMTP.
3. Daily Metrics Dashboard → Slack Notification Every morning, I want to know:
- New signups
- Active users
- Churned accounts
- Stripe revenue
I wrote a Python script that queries my database and posts a summary to Slack:
import psycopg2
import requests
from datetime import datetime, timedelta
def get_metrics():
conn = psycopg2.connect("dbname=saas user=admin password=secret")
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM users WHERE created_at >= now() - interval '1 day'")
new_users = cur.fetchone()[0]
cur.execute("SELECT COUNT(*) FROM users WHERE active=true")
active = cur.fetchone()[0]
conn.close()
return new_users, active
def post_to_slack(new_users, active):
msg = {
"text": f"*Daily Report — {datetime.now().strftime('%Y-%m-%d')}*:\n• New Users: {new_users}\n• Active Users: {active}"
}
requests.post("https://hooks.slack.com/services/XXX", json=msg)
n, a = get_metrics()
post_to_slack(n, a)
I run this script with cron every day at 9 AM. It never fails.
4. Stripe Webhook Listener for Billing Events I didn’t want a full backend server — just a Flask app that receives Stripe events. Here’s how I listen for failed payments:
from flask import Flask, request
import stripe
import logging
app = Flask(__name__)
stripe.api_key = "sk_test_..."
@app.route("/webhook", methods=["POST"])
def webhook():
payload = request.data
sig_header = request.headers.get("stripe-signature")
try:
event = stripe.Webhook.construct_event(
payload, sig_header, "whsec_..."
)
except ValueError as e:
return "Invalid payload", 400
except stripe.error.SignatureVerificationError:
return "Invalid signature", 400
if event["type"] == "invoice.payment_failed":
email = event["data"]["object"]["customer_email"]
logging.warning(f"Payment failed for {email}")
# Notify or flag the user
return {"status": "received"}
It’s saved my life twice. Worth every line.
6. Weekly Usage Reports → PDF via ReportLab Each Sunday, users get a PDF report of their app usage. I generate it with reportlab and send it using Gmail's SMTP.
from reportlab.pdfgen import canvas
def generate_report(username, usage):
filename = f"{username}_report.pdf"
c = canvas.Canvas(filename)
c.drawString(100, 800, f"Weekly Report for {username}")
c.drawString(100, 750, f"Total Logins: {usage['logins']}")
c.drawString(100, 700, f"Actions Taken: {usage['actions']}")
c.save()
return filename
Users love these PDFs. They make the product feel polished and premium.
7. Admin Dashboard with Streamlit I didn’t build an admin UI from scratch. I used Streamlit.
import streamlit as st
import psycopg2
import pandas as pd
conn = psycopg2.connect("dbname=saas user=admin password=secret")
df = pd.read_sql("SELECT * FROM users", conn)
st.title("Admin Dashboard")
st.dataframe(df)
Zero frontend code. Just Python + SQL + beauty.
8. Google Sheets Sync for Financial Reports My co-founder lives in spreadsheets. So I gave her an auto-updating Google Sheet.
import gspread
from oauth2client.service_account import ServiceAccountCredentials
scope = ["https://spreadsheets.google.com/feeds"]
creds = ServiceAccountCredentials.from_json_keyfile_name("creds.json", scope)
client = gspread.authorize(creds)
sheet = client.open("Finance Dashboard").sheet1
sheet.update("A2", [[ "2025-07-27", "$3220", "15 new users" ]])
No more copy-pasting from dashboards. It just… updates.
9. Customer Feedback Parser + Keyword Clustering We get tons of feedback via Typeform and Intercom. I built a tool that clusters them by themes:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
feedback = ["Loved the UI!", "Needs dark mode", "Slow on mobile", "More integrations!"]
vectorizer = TfidfVectorizer(stop_words="english")
X = vectorizer.fit_transform(feedback)
model = KMeans(n_clusters=2)
model.fit(X)
for i, label in enumerate(model.labels_):
print(f"Cluster {label}: {feedback[i]}")
Now I can group issues and prioritize what matters.
10. User Behavior Tracking with Session Logs Want to see which features are used the most? I log every button click and event.
import json
from flask import Flask, request
app = Flask(__name__)
@app.route("/log", methods=["POST"])
def log_event():
with open("session_logs.json", "a") as f:
json.dump(request.json, f)
f.write("\n")
return {"status": "logged"}
I later analyze this with pandas to build heatmaps and flowcharts.
11. Task Scheduler Using APScheduler Need a function to run every hour? Don’t use cron — use this:
from apscheduler.schedulers.background import BackgroundScheduler
import time
scheduler = BackgroundScheduler()
def hourly_task():
print("Running hourly task...")
scheduler.add_job(hourly_task, "interval", hours=1)
scheduler.start()
while True:
time.sleep(60)
My SaaS heartbeat. Never fails. Easy to test. Logs to file. Final Thoughts: Python Is My Backend-as-a-Service Every time I thought I needed a complex backend… I wrote a 40-line Python script instead. The beauty of Python automation is that it scales with you:
- Start with a cron script
- Evolve into Flask or FastAPI
- Add Slack, Stripe, or Google Sheets integrations
- Wrap with Streamlit for instant UI
- Deploy with Docker if needed
If you’re building a SaaS — you don’t need to over-engineer. Just start automating. One Python file at a time.
Pro Tip: Bundle everything into a /scripts and /services folder. Add a Makefile for easy make backup, make report, make dashboard. Suddenly your side project feels like a real product.
Read the full article here: https://medium.com/top-python-libraries/how-i-automated-a-full-saas-workflow-with-nothing-but-python-scripts-e94f370830a2