Skip to content

New signup and first-sale notifications

While the project is small, every new user — and especially the first payment — is an event. Opening a dashboard is a chore, but knowing “someone just signed up right now” is both pleasant and useful. Notifly turns this into a push: a daily digest “N new users” plus a separate loud signal about the very first sale.

  • Server-side only. Hook the signup handler and the payment webhook, not the client — otherwise the token leaks and anyone can push you.
  • Digest vs. storm. If there are lots of signups, don’t push on each one — accumulate a counter and send it once a day. Keep the instant push for payments.
  • The first sale is an event. Mark it separately and louder: it happens once.

We take notify() from the overview page — in Python it’s two lines:

import os, requests
def notify(title, message, priority=5):
requests.post(
f"{os.environ['NOTIFLY_URL']}/message",
params={"token": os.environ["NOTIFLY_TOKEN"]},
json={"title": title, "message": message, "priority": priority},
timeout=5,
)

The signup hook only bumps a counter, and a cron job sends the digest once a day:

const {notify} = require('./notifly');
let signupsToday = 0;
// in the signup handler
app.post('/signup', async (req, res) => {
const user = await createUser(req.body);
signupsToday++;
res.json({ok: true});
});
// daily digest at 21:00 (node-cron)
const cron = require('node-cron');
cron.schedule('0 21 * * *', () => {
if (signupsToday === 0) return; // quiet day — don't make noise
notify('📈 Новые пользователи', `Сегодня зарегистрировалось: ${signupsToday}`, 4);
signupsToday = 0;
});

An instant loud push — only on the very first payment:

const {notify} = require('./notifly');
// in the successful-payment handler (or in the payment provider's webhook)
async function onPaymentSucceeded(payment) {
const isFirstEver = await db.payments.count() === 1; // this is the project's first sale
if (isFirstEver) {
notify(
'🎉 Первая продажа!',
`${payment.amount} ${payment.currency} от ${payment.email}. Оно работает!`,
9
);
} else {
notify('💰 Оплата', `${payment.amount} ${payment.currency} от ${payment.email}`, 5);
}
}
from fastapi import FastAPI
from notifly import notify
app = FastAPI()
signups_today = 0
@app.post("/signup")
async def signup(payload: dict):
global signups_today
await create_user(payload)
signups_today += 1
return {"ok": True}

We hand out the digest on a schedule (APScheduler):

from apscheduler.schedulers.background import BackgroundScheduler
def daily_digest():
global signups_today
if signups_today == 0:
return # quiet day — stay silent
notify("📈 Новые пользователи", f"Сегодня зарегистрировалось: {signups_today}", 4)
signups_today = 0
sched = BackgroundScheduler()
sched.add_job(daily_digest, "cron", hour=21) # every day at 21:00
sched.start()

An instant signal about the first sale:

async def on_payment_succeeded(payment):
is_first_ever = await payments_count() == 1 # first sale of all time
if is_first_ever:
notify(
"🎉 Первая продажа!",
f"{payment['amount']} {payment['currency']} от {payment['email']}. Оно работает!",
9,
)
else:
notify("💰 Оплата", f"{payment['amount']} {payment['currency']} от {payment['email']}", 5)
  • Activation, not just signup. A push when a new user reaches the key action (“created their first project”) is more honest than the signup itself.
  • Plan upgrade. Moving from free to paid is a nice event, priority 5–6.
  • Unsubscribe/refund. Also worth knowing, but quietly (priority 3), so you can ask “what went wrong” in time.
  • Round numbers. The 100th, 1000th user — a separate loud push.
  • Dopamine for the indie developer. “Someone just signed up” and especially “first sale” is the best motivation to keep going.
  • The project’s pulse in your pocket. A once-a-day digest shows the trend without opening a dashboard.
  • No storm. Signups go by digest, payments go point-by-point; the phone doesn’t ring for no reason.