Node.js
The Whisperr server-side SDK for Node.js. The backend is where the highest-signal churn events live — payment failures, cancellations, trial expiry, usage drops — so this is where Whisperr gets its most valuable signal.
npm install @whisperr/nodeQuickstart
Section titled “Quickstart”import { createWhisperr } from "@whisperr/node";
const whisperr = createWhisperr({ apiKey: process.env.WHISPERR_API_KEY! });
// A server-side churn signal:whisperr.track("user_8842", "payment_failed", { amount_cents: 4900, reason: "card_declined" });
// Associate traits / contact channels with a user:whisperr.identify("user_8842", { email: "ada@example.com", traits: { plan: "pro" } });
// Deliver everything before the process exits:await whisperr.shutdown();The user id is always explicit here — unlike the browser SDK, the server has no persisted session to infer it from. Pass the same id you use everywhere else and frontend + backend events land on one timeline.
Express
Section titled “Express”import { createWhisperr } from "@whisperr/node";import { whisperrExpress } from "@whisperr/node/express";
const whisperr = createWhisperr({ apiKey: process.env.WHISPERR_API_KEY! });
app.use(whisperrExpress(whisperr)); // after your auth middleware
app.post("/billing/webhook", (req, res) => { req.whisperr.track("payment_failed", { amount_cents: 4900 }); res.sendStatus(200);});req.whisperr.track() is bound to the request’s user (resolved from common
auth shapes by default, or via resolveUser). For events with no request —
Stripe webhooks, cron jobs — call whisperr.track(userId, …) directly with
the id from your domain data.
Serverless
Section titled “Serverless”In short-lived environments (Lambda, Vercel functions), await whisperr.flush() before returning so queued events aren’t lost when the
runtime freezes.
Reliability
Section titled “Reliability”- Non-blocking.
track()/identify()enqueue and return immediately; delivery happens in the background.await flush()when you need a barrier. - Reliable while the process is alive. In-memory queue, batching, retry
with backoff, per-event idempotency — the full
delivery contract. The queue is not crash-durable:
call
flush()/shutdown()before exit. - Process-friendly. The flush timer is
unref’d so it never keeps your process alive. - Zero runtime dependencies. Uses the global
fetch(Node 18+).
Options
Section titled “Options”| Option | Default | Notes |
|---|---|---|
apiKey |
— | App ingestion key (wrk_…). Required. |
baseUrl |
https://api.whisperr.net |
Ingestion base URL. |
flushAt |
20 |
Flush when this many events are queued. |
flushIntervalMs |
10000 |
Background flush cadence. 0 disables the timer. |
maxQueueSize |
10000 |
Oldest events drop on overflow. |
maxBatchSize |
500 |
Events per batch (hard backend cap is 500). |
maxRetries |
6 |
Consecutive retries before backing off. |
requestTimeoutMs |
10000 |
Per-request timeout. |
disabled |
false |
No-op client (useful in tests). |
debug |
false |
Verbose logging. |
onError |
— | (error) => void for delivery/drop observability. |
fetch |
global fetch |
Inject for tests or custom runtimes. |