Frequently Asked Questions
Quick answers to common questions. For SDK installation issues specifically, see the SDK Installation FAQ.
SDK Setup & Configuration
Why aren't my flows showing up even though init succeeded?
init only fetches your tenant config from the server — flows and content load asynchronously after init resolves. If you're targeting identified users, make sure identify() completes before expecting targeted flows to appear. Enable debug: true to see flow loading and targeting evaluation in the console.
See SDK Initialization & Timing for more detail.
I set contentRefreshInterval to 30 seconds but content only refreshes every 2 minutes. Why?
Values below 120000 ms (2 minutes) are silently clamped to match the server-side cache TTL. Polling faster would return the same cached data. Enable debug: true to see a warning when clamping occurs.
See Content Refresh & Polling.
Should I always enable debug: true?
Yes during development, no in production. Many SDK subsystems fail silently in production mode to avoid disrupting end users. Debug mode is the only way to see these errors. It logs all SDK operations with a [DAP] prefix to the browser console.
DAP("init", {
tenantKey: "wfx_...",
debug: process.env.NODE_ENV !== "production",
});
Identity & Users
What happens if I call identify() before init() finishes?
The call is queued and processed after init completes. However, any events fired between init and identify may carry anonymous context — the backend receives them before it knows who the user is.
Best practice: call identify() immediately after init(), and track custom events only after both complete:
DAP("init", { tenantKey: "wfx_..." });
DAP("identify", { userId: "user-42", traits: { plan: "pro" } });
DAP("track", "feature_used", { feature: "export" });
A user logged in but their flows still show the anonymous experience. What happened?
The identify() call updates local state immediately but also makes an API call to notify the backend. If that API call fails (network error, timeout), the SDK continues with the local state for the current session. On the next page reload, the SDK may revert to anonymous because the backend never received the identity.
Enable debug: true and look for [DAP] identify API call failed messages in the console.
How do I handle user logout?
Call DAP('destroy') on logout to tear down the SDK, stop tracking, and clear session data. Re-initialize and re-identify when the next user logs in:
// On logout
DAP("destroy");
// On next login
DAP("init", { tenantKey: "wfx_..." });
DAP("identify", { userId: "user-99", traits: { role: "viewer" } });
See the User Identity guide for more details.
Events & Analytics
I'm losing events on high-traffic pages. Why?
The SDK queues up to 1,000 events. When the queue overflows, the oldest 10% are dropped. This can happen on pages with many interactive elements when autoTrack is enabled.
To reduce event volume, disable auto-tracking and track only the interactions you care about:
DAP("init", { tenantKey: "wfx_...", autoTrack: false });
DAP("track", "button_click", { buttonId: "checkout" });
Do all my events get delivered when a user closes the tab?
The SDK uses navigator.sendBeacon() on page unload, which has a browser-imposed ~64KB limit. Large batches are split into 60KB chunks. If the total payload exceeds this, some chunks may fail — resulting in partial event delivery.
For critical events, flush manually before navigation:
DAP("flush");
window.location.href = "/next-page";
Flows & Content
My tooltip is pointing to the wrong element (or not appearing at all). What happened?
The SDK uses CSS selectors captured when you authored the tooltip. If the DOM changes between authoring and runtime — dynamic class names, restructured components, framework updates — the selector may no longer match.
The SDK tries progressive fallback (stripping specificity), but significant DOM changes require re-recording. Use the Content Health dashboard to detect broken selectors across your content.
A flow overlay is hidden behind my app's modal. How do I fix the z-index?
The SDK renders overlays at z-index: 2147483647 (the maximum CSS integer value) inside a Shadow DOM. If your modal also uses extreme z-index values, stacking context may cause conflicts.
Solutions:
- Use
onBeforeFlowStartto close competing modals before the flow starts - Adjust your application's modal z-index to stay below
2147483647
DAP("init", {
tenantKey: "wfx_...",
onBeforeFlowStart: function (flowId) {
closeAllModals(); // Close your app's modals before the flow renders
},
});
Why does a "show once" flow keep appearing for some users?
"Show once" frequency gating uses localStorage. Users in private/incognito mode or with blocked storage lose persistence across sessions — each session appears new to the SDK. Additionally, if storage access fails for any reason, the SDK fails open (shows the flow) rather than hiding it.
This is by design: it's better to show a flow twice than to permanently hide it from a user who should see it.
API & Authentication
My API calls work on localhost but fail in production with a 401.
localhost (and 127.0.0.1, *.localhost) always bypasses domain validation for API keys. This means the SDK works locally even if your production domain is not configured.
To fix:
- Go to Settings > API Keys in the dashboard
- Check that your API key's allowed domain matches your production URL exactly
- Enable Allow Subdomains if you serve from
www.example.comandapp.example.com
See the SDK Installation FAQ for a detailed walkthrough.
What happens when I hit a rate limit?
The API returns 429 Too Many Requests with a Retry-After header. The SDK automatically retries with exponential backoff. Rate limits are enforced per API server instance (in-memory), not globally — so the effective limit may be slightly higher than documented in multi-instance deployments.
See the rate limit table for limits by route type.
I'm sending the same request twice and getting a 409 Conflict.
You're likely including an Idempotency-Key header. If a request with the same key is still processing when the duplicate arrives, the API returns 409 Conflict. Wait for the first request to complete before retrying, or omit the Idempotency-Key header if you don't need idempotency.
A completed request with the same key returns the cached response (with an idempotency-replay: true header) rather than processing again.
Plans & Limits
I hit my MAU limit. Are existing users locked out?
No. MAU (Monthly Active Users) gating only blocks genuinely new users who have never been seen before. Existing active users continue to work normally even when the limit is reached. Upgrade your plan in Settings > Plan to allow new users again.
I got a warning about approaching feature limits. What happens at 100%?
The platform sends a warning email at 80% usage for any feature limit (MAU, flows, content items, etc.). At 100%, new creation of that resource type is blocked — but all existing content continues to function normally.
To resolve: upgrade your plan in Settings > Plan, or delete unused content to free up capacity. A limit value of -1 in your plan means unlimited — no cap is enforced for that feature.