Open-Source Cold Email & Warmup Platform
Warmbly is the open-source platform for cold email: warm your mailboxes, send campaigns, and triage every reply. Run it on our cloud or self-host the whole thing.
Every 5th visit free: a stamp card on the customer’s phone number. One button at the counter, the card on WhatsApp. Free to start.
Translation pending
MarketingTypeScript · CSSGitHub @sidhardevOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Turn walk-ins into regulars. A simple loyalty program for neighbourhood shops: a stamp card on the customer's phone number — every few visits, a free reward. Live at www.regularhood.in (formerly Habiturn, at habiturn.sidhar.me, which now redirects there).
server/data/loyalty.db, Node's built-in node:sqlite)A digital stamp card: every N visits → one reward (e.g. every 5 visits → free chai). A customer is just a phone number — no names, QR codes, points, PINs or sign-up.
/w/<link>: their stamps, what's left to the reward and their visits; English / हिंदी, no app
or login. Old counter-poster QRs (/join/<code>) show the shop's rule; old bill QR and PIN login links explain
there's nothing to scan any moreShops from before the stamp card keep their customers: each customer's past bills became stamps (up to one short of the reward, so their next visit earns it), and the shop's cheapest reward became its one reward.
/admin is the platform owner's panel: every shop's health (active / sleeping / new / never started / paused), a
"needs a call" list of shops that went quiet or never set up their card, and per-shop numbers (their card rule,
customers, visits and rewards given, customers who came back, rewards waiting). Support actions: a one-time password
reset link (24 hours), pausing a shop (no logins or new visits — customers' cards still open) and notes. Every admin action is in the admin log. Admins see counts and
totals only, never customers' names or phone numbers.
Admins are created only on the server — never from the website:
npm run create-admin -w server -- [email protected]
It asks for the password (hidden, at least 12 characters). Run it again to set a new password.
Needs Node.js 22.16 or newer.
One command (or double-click start.bat on Windows):
npm install # first time only
npm start
Open http://localhost:4000. npm start builds the website and API and runs them as one server on port 4000.
While changing code (live reload):
npm run dev
Also open http://localhost:4000 — the website dev server forwards /api to the API on port 4001.
Putting it online: see DEPLOY.md (VPS with Docker + automatic HTTPS, or Railway). The database is copied
to data/backups once a day (last 14 kept). Behind a proxy, set TRUST_PROXY=1.
Set JWT_SECRET in production (otherwise one is generated into server/data/), and optionally PORT and DB_FILE. Back up server/data/ — it is the whole database.
All routes except signup/login need Authorization: Bearer <token>.
| Method | Path | |
|---|---|---|
| POST | /api/auth/signup | { name, email, password } → { token, merchant } |
| POST | /api/auth/login | { email, password } → { token, merchant } |
| GET / PATCH | /api/me | shop details / update { name, phone, visitsNeeded, rewardName, inactiveDays } |
| POST | /api/visits | { phone, tz } → one visit (once a day per number); { already, isNew, visitId, customer } |
| POST | /api/customers/:id/reward | the shop handed over the reward; the card starts again |
| POST | /api/transactions/:id/undo | take back one visit or reward |
| GET | /api/today?tz=330 | today's visits and rewards |
| GET | /api/customers | list (?q= search, ?filter=reward or inactive) |
| GET | /api/customers/suggest?digits= | known numbers starting with these digits (4+) |
| GET / PATCH / DELETE | /api/customers/:id | customer with visit history / change number { phone } / delete with history |
| GET | /api/share | customers and referrals, for the share image |
| GET | /api/activity?limit=200 | activity log |
| GET | /api/export/customers.csv, /api/export/transactions.csv | spreadsheet downloads |
| GET | /api/public/wallet/:token | the customer's stamp card (no login) |
| GET | /api/public/wallet/:token/manifest | per-card app manifest for "Add to Home Screen" |
| GET | /api/public/join/:code | the shop's rule, for old counter-poster QR codes (no login) |
The database schema upgrades itself on start (PRAGMA user_version), keeping existing data.