SDK Reference
Doppler JS SDK
A tiny (~5KB) JavaScript SDK that tells Doppler what your users are doing β so Doppler can automatically recover them when they go quiet.
Installation
Install from npm. Zero dependencies. Works with any JS framework.
npm install dopplerjsQuick Start
The entire integration, start to finish.
import { doppler } from 'dopplerjs';
// 1. Initialize once when your app loads
doppler.init({ clientKey: 'pk_client_YOUR_KEY' });
// 2. After the user logs in, tell Doppler who they are
doppler.identify('jane@example.com', { name: 'Jane' });
// 3. Tell Doppler when something important happens
doppler.coreAction(); // <- The most important call
// 4. Track revenue (optional but powerful)
doppler.trackRevenue(29, 'Pro Plan');That's it. Doppler now monitors this user and will send them a recovery email if they go silent.
API Keys
Doppler gives you two keys. Use the right one in the right place.
Safe to use in browser code. Starts with pk_client_. Used for all SDK calls.
Never put this in the browser. Use in your backend or server actions only. Starts with sk_.
Methods
doppler.init()
RequiredSet up the SDK. Call this once when your app starts.
Think of this like turning Doppler on. You must call init() before anything else. Give it your Client Key and Doppler is ready to go. It also automatically restores the last known user from localStorage β so if someone refreshes the page, they stay identified.
Signature
doppler.init({
clientKey: string, // Required. Your public client key.
apiUrl?: string, // Optional. Custom API endpoint.
testMode?: boolean, // Optional. Sends all emails to YOUR inbox.
avgRevenuePerUser?: number // Optional. Monthly revenue per user in USD.
})Example
import { doppler } from 'dopplerjs';
doppler.init({
clientKey: 'pk_client_abc123',
testMode: process.env.NODE_ENV !== 'production',
avgRevenuePerUser: 49 // $49/month per user
});Call init() before identify() or any tracking methods. Usually goes in your root layout or App.tsx.
doppler.identify()
RequiredTell Doppler who is logged in right now.
After a user signs in, call identify() with their email. This links everything Doppler tracks to a real person. Without this, Doppler doesn't know who to email. Optionally pass their name and plan β the AI uses this to write more personal recovery emails.
Signature
doppler.identify(
email: string, // Required. The user's email address.
options?: {
name?: string, // Optional. Personalizes recovery emails.
plan?: string, // Optional. e.g. "free" | "pro"
planDaysLeft?: number // Optional. Days left in their trial.
}
)Example
// After login or session restore:
doppler.identify('jane@example.com', {
name: 'Jane Smith',
plan: 'trial',
planDaysLeft: 8 // Triggers trial-ending email flows
});Call identify() every time the user logs in or when you restore their session. Safe to call multiple times with the same email.
doppler.trackRevenue()
Record a revenue event (payment, upgrade, renewal).
Tell Doppler whenever a user pays. This powers the revenue metrics on your dashboard β showing exactly how much money Doppler recovered. You'll see 'Recovered Revenue' and 'Revenue at Risk' figures in your overview.
Signature
doppler.trackRevenue(
amount: number, // Required. Amount in USD (e.g. 49 = $49).
label?: string // Optional. Label for this revenue event.
)Example
// When a user pays or upgrades:
doppler.trackRevenue(49, 'Pro Plan - Monthly');
doppler.trackRevenue(499, 'Pro Plan - Annual');
doppler.trackRevenue(29, 'Seat Added');Lifecycle Milestones
These methods tell Doppler how far a user has gotten in your product. Doppler uses this to decide which recovery email to send.
| Milestone | Recovery Flow Triggered |
|---|---|
| signed up only | Flow A β 'You haven't tried X yet' |
| onboarding done | Flow A β nudge toward core action |
| projectCreated() | Flow B β 'You set up but drifted away' |
| coreAction() | Flow B / E β usage drop detection begins |
| coreActionRepeat() | Flow E β active user, watch for drops |
doppler.onboardingStarted()
User just began your onboarding flow.
Call this when the user starts your product tour, welcome wizard, or setup guide. If they never finish onboarding, Doppler knows they got stuck early and sends a friendly push to come back.
Signature
doppler.onboardingStarted(): Promise<void>Example
// When your setup wizard starts:
function WelcomeWizard() {
useEffect(() => {
doppler.onboardingStarted();
}, []);
}doppler.onboardingCompleted()
User finished your onboarding.
Call this when the user completes your setup steps or dismisses the welcome flow. It moves them past the 'new user' stage and tells Doppler they're ready to get real value.
Signature
doppler.onboardingCompleted(): Promise<void>Example
// When the last onboarding step is done:
async function finishOnboarding() {
await saveUserSettings();
doppler.onboardingCompleted();
router.push('/dashboard');
}doppler.projectCreated()
User created their first 'thing' in your product.
This milestone means the user has done the core setup β created a project, workspace, campaign, or whatever the main entity is in your product. If they disappear after this, Doppler sends a 'you set up but drifted away' email.
Signature
doppler.projectCreated(): Promise<void>Example
// After user creates their first workspace/project:
async function createProject(data) {
const project = await api.post('/projects', data);
doppler.projectCreated();
return project;
}The name says "project" but it means whatever your main entity is β board, campaign, store, team, pipeline, etc.
doppler.coreAction()
Most ImportantUser just hit your product's AHA moment.
This is the single most important call in the SDK. The 'core action' is the one thing users must do to get real value β sending an invoice, publishing a page, running a report, making a sale. Doppler starts tracking how often they do it. If they stop, it detects the drop and sends recovery email.
Signature
doppler.coreAction(): Promise<void>Example
// Map this to your AHA moment:
// Invoice app:
async function sendInvoice(invoice) {
await api.post('/invoices/send', invoice);
doppler.coreAction(); // <- user got value!
}
// Analytics app:
async function generateReport() {
const report = await api.get('/reports/generate');
doppler.coreAction();
return report;
}Only one core action per product. Ask yourself: what's the one thing users do right before they become loyal? That's it.
doppler.coreActionRepeat()
User did the core action again. They're engaged.
Call this every time the user repeats their core action. Doppler measures the frequency. If the cadence drops (e.g. used to be daily, now gone a week), Doppler flags them as 'at risk' and starts recovery. This is how Doppler catches churn before it happens.
Signature
doppler.coreActionRepeat(): Promise<void>Example
async function sendInvoice(invoice) {
await api.post('/invoices/send', invoice);
if (user.invoiceCount === 0) {
doppler.coreAction(); // First time
} else {
doppler.coreActionRepeat(); // Repeating = healthy
}
user.invoiceCount++;
}doppler.checkpoint()
BetaSend a custom milestone with any name.
checkpoint() lets you define your own milestone names outside the built-in lifecycle set. It calls the same internal milestone pipeline as onboardingStarted() and coreAction() β so any named checkpoint you send gets recorded in the user's journey and appears in your dashboard.
Signature
doppler.checkpoint(name: string): Promise<void>Example
doppler.checkpoint('store_connected');
doppler.checkpoint('first_sale_made');
doppler.checkpoint('team_of_5_reached');
doppler.checkpoint('api_integrated');checkpoint() is in beta. The Doppler engine does not yet fully classify custom checkpoints into recovery flows β it records them but Flow A/B/E triggers are not yet wired to custom names. Use the named milestone methods (coreAction, projectCreated, etc.) for recovery-critical events.
Temporarily degraded in some regions.
Due to escalating geopolitical instability following the IranβUS conflict, cross-border network routing through the UAE, Iran, and surrounding MENA infrastructure has become unreliable. Doppler's event ingestion nodes route through a shared Anycast backbone that passes through affected peering points β checkpoint() events originating from or transiting through these regions may be silently dropped at the network edge rather than queued and retried.
We are migrating to a geo-redundant ingestion pipeline with regional failover. Until then, do not rely on checkpoint() for critical recovery flows β use the named milestone methods instead. ETA: Q2 2026.
Utilities
doppler.reset()
Log the user out of Doppler. Call this on sign-out.
When a user signs out, call reset(). It clears the stored email, stops the heartbeat, and wipes all session state. If you skip this, the next user who opens the browser might be tracked under the previous user's session.
Signature
doppler.reset(): voidExample
// In your logout function:
async function logout() {
await auth.signOut();
doppler.reset(); // <- always call this on logout
router.push('/login');
}doppler.captureChurnFeedback()
Capture why a user is leaving or cancelling.
When a user cancels, ask them why and pass it to Doppler. This surfaces as patterns in your dashboard β e.g. '40% say it's too expensive'. Use this insight to fix the actual reasons for churn.
Signature
doppler.captureChurnFeedback(
reason: 'too_expensive' | 'too_complex' | 'found_alternative' | 'not_needed' | 'other',
comment?: string
)Example
function CancelModal({ onClose }) {
const [reason, setReason] = useState('too_expensive');
const [comment, setComment] = useState('');
const handleCancel = async () => {
await api.delete('/subscription');
doppler.captureChurnFeedback(reason, comment);
onClose();
};
}doppler.setTestMode()
Toggle test mode at runtime.
In test mode, all recovery emails go to YOUR inbox instead of real users. Lets you see exactly what Doppler would send without spamming actual users.
Signature
doppler.setTestMode(enabled: boolean): voidExample
// Enabled in dev automatically via init():
doppler.init({
clientKey: 'pk_client_abc',
testMode: process.env.NODE_ENV !== 'production'
});
// Or toggle dynamically:
doppler.setTestMode(true); // my inbox
doppler.setTestMode(false); // real usersdoppler.isIdentified()
Check if a user is currently identified.
Returns true if the SDK currently knows who the user is. Useful for conditional logic or for debugging to confirm your identify() call worked.
Signature
doppler.isIdentified(): boolean
doppler.getIdentifiedEmail(): string | nullExample
if (doppler.isIdentified()) {
doppler.coreAction();
}
// Debug:
console.log(doppler.getIdentifiedEmail()); // "jane@example.com"Questions? Email doppler@arinten.com