Bellring is a whitelabel SaaS for sales-team celebration notifications — the sales-floor
bell-ringing ritual, online: closing a deal fires unmissable, team-wide popups. This repo is the
backend half (bellring-server); the browser surface lives in the paired
bellring-extension repo.
It is a Node.js / Express application that:
- receives CRM webhooks (currently LeadSquared) and broadcasts them to connected extension clients in real time over WebSocket,
- issues OTP-based authentication (email OTP → JWT token), and
- sends OTP emails via SendGrid.
Reference customer: the live deployment serves ~300 Business Development Executives (BDEs) at Coding Ninjas, with the OTP allowlist scoped to
@codingninjas.com. The whitelabel / multi-tenant pivot is tracked inCLAUDE.md(Roadmap).
The backend is deployed on Render (https://sales-notification-backend.onrender.com),
auto-deploying on main push via the Procfile. (Bellring-branded URL pending domain choice.)
- Real-Time Notifications
- Receives webhook payloads at
/webhookand broadcasts them to connected clients via WebSocket. - Supports three notification types:
sale_made— sale notifications with BDE name, product, and manager name.notification— general announcements.private— private messages targeted to a specific user (by email).
- Receives webhook payloads at
- User Authentication
- Generates and sends OTPs to
@codingninjas.comemails (/request-otp). - Verifies OTPs and issues a signed JWT token (
/verify-otp).
- Generates and sends OTPs to
- Security
- Webhook endpoint secured with a Bearer token (
WEBHOOK_TOKEN). - API endpoints and WebSocket connections secured with JWT authentication.
- CORS and WebSocket origin validation restricted to the Chrome extension's origin.
- Rate limiting on API endpoints and per-IP WebSocket connection limiting.
- Webhook endpoint secured with a Bearer token (
| File | Description |
|---|---|
server.js |
Main backend application — Express, WebSocket, API endpoints (single-file monolith). |
package.json |
Dependencies and scripts. |
Procfile |
Render deployment entrypoint (web: node server.js). |
.env.example |
Template for environment variables (copy to .env for local dev — never committed). |
express— web framework.ws— WebSocket library for real-time communication.@sendgrid/mail— SendGrid client for sending OTP emails.jsonwebtoken— signs and verifies JWT auth tokens.cors— CORS middleware with origin restrictions.express-rate-limit— API rate limiting.@sentry/node— error reporting.dotenv— loads environment variables from.env(local development).
- Node.js ≥ 16 (CI runs on Node 20).
- A SendGrid account with an API key (for OTP emails).
- A LeadSquared account (or other CRM) to send webhook notifications.
- A Render account for deployment (optional for local dev).
- Clone:
git clone https://github.com/Cramraika/bellring-server.git cd bellring-server - Install:
npm install
- Configure environment — copy
.env.exampleto.envand fill in:SENDGRID_API_KEY— your SendGrid API key.WEBHOOK_TOKEN— secure token for CRM webhook authentication.EXTENSION_ID— your Chrome extension ID (for CORS).JWT_SECRET— secret used to sign/verify JWT tokens.PORT— port to run on (default3000).
- Run:
The server listens on
npm start # production npm run dev # with nodemon (auto-reload)
http://localhost:3000.
The repo auto-deploys on main push via the Procfile (web: node server.js). Set the same
environment variables (SENDGRID_API_KEY, WEBHOOK_TOKEN, EXTENSION_ID, JWT_SECRET, PORT)
in the Render service's Environment tab.
Point your CRM webhook at:
- URL:
https://sales-notification-backend.onrender.com/webhook - Method: POST
- Headers:
Authorization: Bearer <WEBHOOK_TOKEN>,Content-Type: application/json - Payload examples:
{ "type": "sale_made", "bdeName": "Jane Doe", "product": "Job Bootcamp", "managerName": "Sales Team" }{ "type": "notification", "message": "Team meeting at 3 PM" }{ "type": "private", "email": "user@codingninjas.com", "message": "Submit your report" }
Generates and sends an OTP to a @codingninjas.com email.
- Body:
{ "email": "user@codingninjas.com" } - Responses:
200OTP sent ·400invalid email ·500send failure.
Verifies the OTP and returns a signed JWT token.
- Body:
{ "email": "user@codingninjas.com", "otp": "123456" } - Responses:
200{ "token": "<jwt>", ... }·400invalid/expired OTP.
Receives CRM notifications and broadcasts them to WebSocket clients.
- Headers:
Authorization: Bearer <WEBHOOK_TOKEN> - Responses:
200received ·400missingtype·401unauthorized.
Health check.
Keepalive (keeps the Render free-tier instance awake). Returns { "status": "alive" }.
Admin-only connection/notification stats (Bearer-gated).
- URL:
wss://sales-notification-backend.onrender.com/ws?token=<jwt> - Auth: requires a
token(JWT) query parameter, obtained from/verify-otp. - Messages: the client sends
{ "type": "ping" }periodically and receives{ "type": "pong" }; the backend broadcasts/webhookpayloads (sale_made,notification,private).
- JWT authentication — API endpoints and WebSocket connections require a JWT issued by
/verify-otp. - Webhook authentication —
WEBHOOK_TOKENsent asBearer <WEBHOOK_TOKEN>. - CORS / origin validation — restricted to
chrome-extension://${EXTENSION_ID}. - Rate limiting — API endpoints limited to 100 requests per IP per 15 minutes; WebSocket connections limited per IP.
- Environment variables — all secrets supplied via env vars, never hardcoded.
- OTPs are stored in-memory and lost on restart; tokens are file-backed (
tokens.json). A database (e.g. Postgres) is planned for the multi-tenant pivot — seeCLAUDE.mdRoadmap. - Rate limiting is IP-based, which may affect users behind shared/corporate IPs.
Proprietary — © 2026 Vagary Labs. All rights reserved. See LICENSE.