How to Turn Your Slack Bot Side Project Into a Paid SaaS Product
You built a Slack bot to scratch your own itch. It auto-summarizes standup threads, routes support tickets, or pulls analytics on demand. A few friends asked for it. A couple of strangers on Reddit want it. And now you're realizing you could actually charge for this thing β but you have no idea where to start.
The gap between "cool bot I run on a VPS" and "product people pay for" is smaller than it looks. You don't need to rewrite everything. You need a billing layer, a way to isolate each customer's data, and a pricing page. That's mostly it.
What you'll learn
- How to pick a pricing model that fits a Slack bot
- How to wire up Stripe billing without rebuilding your bot
- How to handle multi-tenant access so one customer's data doesn't bleed into another's
- How to get your first paying customers without a marketing budget
- What the Slack App Directory requires if you want to go that route
Prerequisites
This article assumes your bot already works. You have a codebase, it's deployed somewhere (a VPS, Fly.io, Railway, whatever), and it responds to Slack events or slash commands. You don't need to know Stripe deeply β the examples below use Stripe Billing, but the concepts apply to any payment processor.
Pick a Pricing Model Before You Write a Line of Code
The worst thing you can do is add billing infrastructure before deciding what you're actually billing for. There are three models that work well for Slack bots, and the right one depends on what your bot does.
Per-workspace flat fee
The simplest option. One team installs your bot, they pay $X per month regardless of how many users they have. This is easy to reason about for customers and easy to implement. It works well when the value your bot delivers is roughly the same for any team size β a bot that posts a daily digest, for example.
Per-seat pricing
You charge based on the number of active users in the workspace. Slack itself uses this model. It scales naturally with the customer's growth, which means your revenue grows too. The downside: you need to track active users, which adds complexity.
Usage-based pricing
You charge based on what the bot actually does β API calls made, documents processed, messages sent. This is the right model if your bot has a clear, measurable unit of value and if your underlying costs scale with usage (for example, if you're calling an LLM API per request). The tradeoff is that revenue becomes harder to predict.
For most side projects making the jump to paid, start with per-workspace flat fee. It's the lowest friction model for both you and your customers. You can always add seats or usage tiers later once you understand how people actually use the product.
Design Your Tenant Model First
A "tenant" in SaaS terms is a single customer β one Slack workspace that installed your bot. Right now, your bot probably has no concept of tenants. It just does its thing. Before you can charge multiple workspaces, you need to isolate their data from each other.
The minimum viable tenant model is a workspaces table (or collection, depending on your stack) that stores everything you need to identify and serve each customer.
CREATE TABLE workspaces (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slack_team_id TEXT NOT NULL UNIQUE,
slack_bot_token TEXT NOT NULL,
stripe_customer_id TEXT,
stripe_subscription_id TEXT,
plan TEXT NOT NULL DEFAULT 'free',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
Every time your bot receives an event from Slack, the payload includes a team_id. Use that to look up the workspace record, check if the subscription is active, and then decide whether to fulfill the request. If the subscription is expired or the workspace is on the free tier, respond with a message that links to your pricing page.
Wire Up Stripe in a Weekend
Stripe's setup is more approachable than its documentation length implies. Here's the minimum you need.
1. Create a product and price in Stripe
Log into the Stripe dashboard, create a product (your bot), and add a recurring price β say, $29/month per workspace. Copy the price ID; it looks like price_1ABC123.... You'll use this in your code.
2. Create a checkout session when someone wants to subscribe
When a workspace owner clicks "Upgrade" on your pricing page (or types a slash command like /subscribe), redirect them to a Stripe Checkout session. Here's how that looks in Python with the stripe library:
import stripe
stripe.api_key = "sk_live_..."
def create_checkout_session(slack_team_id: str, email: str) -> str:
session = stripe.checkout.Session.create(
payment_method_types=["card"],
line_items=[{"price": "price_1ABC123...", "quantity": 1}],
mode="subscription",
success_url="https://yourdomain.com/success?team={}".format(slack_team_id),
cancel_url="https://yourdomain.com/pricing",
customer_email=email,
metadata={"slack_team_id": slack_team_id},
)
return session.url
3. Listen to Stripe webhooks
This is the part people skip and then spend a weekend debugging. When a payment succeeds, Stripe sends a webhook to an endpoint you define. You need to handle at minimum three events: checkout.session.completed (new subscriber), customer.subscription.updated (plan changes), and customer.subscription.deleted (cancellations).
from flask import Flask, request, abort
import stripe
import json
app = Flask(__name__)
@app.route("/stripe/webhook", methods=["POST"])
def stripe_webhook():
payload = request.data
sig_header = request.headers.get("Stripe-Signature")
try:
event = stripe.Webhook.construct_event(
payload, sig_header, "whsec_..."
)
except stripe.error.SignatureVerificationError:
abort(400)
if event["type"] == "checkout.session.completed":
session = event["data"]["object"]
team_id = session["metadata"]["slack_team_id"]
customer_id = session["customer"]
subscription_id = session["subscription"]
# Update your workspaces table here
activate_workspace(team_id, customer_id, subscription_id)
elif event["type"] == "customer.subscription.deleted":
subscription = event["data"]["object"]
# Find the workspace by stripe_subscription_id and downgrade it
deactivate_workspace_by_subscription(subscription["id"])
return "", 200
Use Stripe's CLI locally (stripe listen --forward-to localhost:5000/stripe/webhook) to test webhooks before you deploy. This will save you a lot of frustration.
Build a Minimal Pricing Page
You don't need a fancy marketing site. You need a page that answers three questions: what does the bot do, how much does it cost, and how do I get started. A single HTML page hosted on your domain is fine to start.
The page should have a clear call to action that either triggers the OAuth install flow (for new workspaces) or the Stripe checkout flow (for existing free-tier workspaces). Keep it simple. You can add testimonials, FAQs, and feature comparisons after you have paying customers.
One thing worth doing early: add a free tier or a trial. A 14-day free trial removes the biggest objection for a bot no one has heard of yet. Stripe supports trials natively β just add trial_period_days=14 to your price or checkout session.
Gate Features Based on Subscription Status
Every inbound Slack event should go through a single authorization check. Don't scatter subscription logic across your handlers β you'll miss something and accidentally give away paid features.
def get_workspace_or_deny(team_id: str, say):
workspace = db.get_workspace(team_id)
if workspace is None:
say("This workspace isn't set up yet. Visit https://yourdomain.com to get started.")
return None
if workspace.plan == "free" and workspace.trial_expired:
say("Your trial has ended. Upgrade at https://yourdomain.com/pricing to keep using the bot.")
return None
return workspace
Put this at the top of every event handler. It keeps your authorization logic in one place and makes it easy to add new plan tiers later without hunting through the codebase.
Getting Your First Paying Customers
This is where most side project founders get stuck. They build the billing, update the README, and wait. Nobody comes. Here's what actually works for a niche tool like a Slack bot.
- Post in communities where your target users already are. If your bot handles engineering metrics, post in engineering management Slack groups and subreddits. Show what it does, not what it is.
- Find people who have complained about the problem your bot solves. Search Twitter/X, Reddit, and Hacker News for the pain point. Reply directly with your tool.
- DM the people who starred similar open-source repos. GitHub's star list is public. People who starred a competing project are pre-qualified leads.
- Offer to install it for free for the first five teams in exchange for feedback. Five real users give you more insight than a hundred sign-up form submissions.
- Write one honest post about what you built and why. Post it on your personal site, dev.to, or Hacker News Show HN. It doesn't need to go viral β it just needs to reach the right five people.
Should You List on the Slack App Directory?
The Slack App Directory is Slack's official marketplace. Getting listed there can drive organic installs, but it comes with real requirements: your app must use Slack's OAuth flow properly, handle event subscriptions correctly, pass a security review, and maintain a certain level of uptime. The review process takes time and requires you to have a public privacy policy and terms of service.
For a side project just starting to charge, the directory is probably not your first move. Get a handful of paying customers through direct outreach first. Validate that people actually pay and stick around. Then go through the directory review with confidence that the product is solid.
The exception: if your bot is genuinely general-purpose and the discovery traffic from the directory is meaningful for your category, start the application process early. It can take weeks to get approved.
Common Pitfalls to Avoid
Not verifying Slack request signatures. Anyone who knows your webhook URL can send fake events. Slack signs every request with a secret. Verify it on every inbound request, not just occasionally.
Storing Slack tokens insecurely. Bot tokens give access to a workspace's messages and files. Store them encrypted at rest, not in plaintext in a database column or environment variable that gets logged.
Building too much before charging. The instinct to add one more feature before you ask for money is a trap. Charge for what you have now. Features can come after you have evidence people want the product.
Ignoring failed payments. Stripe will retry failed payments automatically, but it won't handle communication for you. Set up Stripe's dunning emails (under Billing settings) so customers get notified before their access gets cut off.
Conflating workspace ID with user ID. Slack has team IDs, workspace IDs, user IDs, and bot IDs. For billing purposes, the unit is the workspace (team_id). Make sure your lookup logic is consistent or you'll create billing bugs that are miserable to debug.
Next Steps
You've built the hard part already. The path from "bot that runs on my server" to "bot people pay for" is mostly plumbing and a bit of outreach. Here's what to do this week:
- Add the workspaces table and update your event handlers to look up the workspace before doing anything else.
- Create a Stripe product and price, set up a checkout session endpoint, and handle the three core webhook events.
- Write a one-page pricing page with a clear install button and a 14-day trial offer.
- Find ten people who have the problem your bot solves and show them what it does. Don't wait for them to find you.
- Add Slack request signature verification if you haven't already β this is a security requirement, not optional.
You don't need a co-founder, a pitch deck, or a launch event. You need a billing table, a webhook endpoint, and a pricing page. Start there.
π€ Share this article
Sign in to saveRelated Articles
Comments (0)
No comments yet. Be the first!